summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlsces <lester@lsces.co.uk>2026-03-05 10:18:56 +0000
committerlsces <lester@lsces.co.uk>2026-03-05 10:18:56 +0000
commit1bdc7cd5c95561622103cc470dba0af041996c28 (patch)
treedefa47a60d436b627443651464d2cf2a0f0ab8de
parentdfee5db3888ab04148febf24b3ce641e6c200bde (diff)
downloadilluminate-firebird-1bdc7cd5c95561622103cc470dba0af041996c28.tar.gz
illuminate-firebird-1bdc7cd5c95561622103cc470dba0af041996c28.tar.bz2
illuminate-firebird-1bdc7cd5c95561622103cc470dba0af041996c28.zip
Shuffle the driver into an Illuminate namespace to allow other elements to be added later, if required. Just using a Firebird namespace did not fell right?
-rwxr-xr-xsrc/illuminate/FirebirdConnection.php171
-rwxr-xr-xsrc/illuminate/FirebirdConnector.php58
-rwxr-xr-xsrc/illuminate/FirebirdServiceProvider.php23
-rwxr-xr-xsrc/illuminate/Query/FirebirdBuilder.php126
-rwxr-xr-xsrc/illuminate/Query/Grammars/FirebirdGrammar.php256
-rw-r--r--src/illuminate/Query/Processors/FirebirdProcessor.php40
-rwxr-xr-xsrc/illuminate/Schema/FirebirdBuilder.php10
-rwxr-xr-xsrc/illuminate/Schema/Grammars/FirebirdGrammar.php665
8 files changed, 1349 insertions, 0 deletions
diff --git a/src/illuminate/FirebirdConnection.php b/src/illuminate/FirebirdConnection.php
new file mode 100755
index 0000000..b279632
--- /dev/null
+++ b/src/illuminate/FirebirdConnection.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Firebird\Illuminate;
+
+use Illuminate\Database\Connection as DatabaseConnection;
+use Illuminate\Support\Collection;
+use Firebird\Illuminate\Query\FirebirdBuilder as FirebirdQueryBuilder;
+use Firebird\Illuminate\Query\Grammars\FirebirdGrammar as FirebirdQueryGrammar;
+use Firebird\Illuminate\Query\Processors\FirebirdProcessor as FirebirdQueryProcessor;
+use Firebird\Illuminate\Schema\FirebirdBuilder as FirebirdSchemaBuilder;
+use Firebird\Illuminate\Schema\Grammars\FirebirdGrammar as FirebirdSchemaGrammar;
+
+class FirebirdConnection extends DatabaseConnection
+{
+ /**
+ * The last inserted ID generated by the server.
+ * RETURNED by the insert statement
+ *
+ * @var string|int|null
+ */
+ protected $lastInsertId;
+
+ /**
+ * Get the default query grammar instance.
+ *
+ * @return \Illuminate\Database\Query\Grammars\Grammar
+ */
+ protected function getDefaultQueryGrammar(): FirebirdQueryGrammar
+ {
+ return new FirebirdQueryGrammar($this);
+ }
+
+ /**
+ * Get the default post processor instance.
+ *
+ * @return \Illuminate\Database\Query\Processors\Processor
+ */
+ protected function getDefaultPostProcessor(): FirebirdQueryProcessor
+ {
+ return new FirebirdQueryProcessor;
+ }
+
+ /**
+ * Get a schema builder instance for this connection.
+ *
+ * @return \Firebird\Illuminate\Schema\Builder
+ */
+ public function getSchemaBuilder(): FirebirdSchemaBuilder
+ {
+ if (is_null($this->schemaGrammar)) {
+ $this->useDefaultSchemaGrammar();
+ }
+
+ return new FirebirdSchemaBuilder($this);
+ }
+
+ /**
+ * Get the default schema grammar instance.
+ *
+ * @return \Firebird\Illuminate\Schema\Grammars\FirebirdGrammar
+ */
+ protected function getDefaultSchemaGrammar()
+ {
+ return new FirebirdSchemaGrammar($this); // $this->withTablePrefix()
+ }
+
+ /**
+ * Get the connection's last insert ID.
+ *
+ * @return string|int|null
+ */
+ public function getLastInsertId()
+ {
+ return $this->lastInsertId;
+ }
+
+ /**
+ * Get a new query builder instance.
+ *
+ * @return \Firebird\IlluminateQuery\FirebirdBuilder
+ */
+ public function query()
+ {
+ return new FirebirdQueryBuilder(
+ $this, $this->getQueryGrammar(), $this->getPostProcessor()
+ );
+ }
+
+ /**
+ * Run an insert statement against the database.
+ *
+ * @param string $query
+ * @param array $bindings
+ * @param string|null $sequence
+ * @return array
+ */
+ public function insert($query, $bindings = [], $sequence = null)
+ {
+ return $this->run($query, $bindings, function ($query, $bindings) use ($sequence) {
+ if ($this->pretending()) {
+ return true;
+ }
+
+ $statement = $this->getPdo()->prepare($query);
+
+ $this->bindValues($statement, $this->prepareBindings($bindings));
+
+ $this->recordsHaveBeenModified();
+
+ $result = $statement->execute();
+
+ // Fetch the RETURNING clause result to get the actual ID used
+ if ($result) {
+ $row = $statement->fetch(\PDO::FETCH_ASSOC);
+ if ($row) {
+ // Get the first (and typically only) returned value
+ $this->lastInsertId = reset($row);
+ }
+ }
+
+ return $result;
+ });
+ }
+
+ /**
+ * Run a select statement against the database.
+ *
+ * @param string $query
+ * @param array $bindings
+ * @param bool $useReadPdo
+ * @return array
+ */
+ public function select($query, $bindings = [], $useReadPdo = true)
+ {
+ return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
+ if ($this->pretending()) {
+ return [];
+ }
+
+ // For select statements, we'll simply execute the query and return an array
+ // of the database result set. Each element in the array will be a single
+ // row from the database table, and will either be an array or objects.
+ $statement = $this->prepared(
+ $this->getPdoForSelect($useReadPdo)->prepare($query)
+ );
+
+ $this->bindValues($statement, $this->prepareBindings($bindings));
+
+ $statement->execute();
+
+ $result = [];
+ while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
+ $lowerCaseRow = array_change_key_case((array) $row, CASE_LOWER);
+ $result[] = (object) $lowerCaseRow;
+ }
+ return count($result) > 0 ? $result : [];
+ });
+ }
+
+ /**
+ * Execute a stored procedure.
+ *
+ * @param string $procedure
+ * @param array $values
+ * @return Collection
+ */
+ public function executeProcedure($procedure, array $values = []): Collection
+ {
+ return $this->query()->fromProcedure($procedure, $values)->get();
+ }
+}
diff --git a/src/illuminate/FirebirdConnector.php b/src/illuminate/FirebirdConnector.php
new file mode 100755
index 0000000..3517f34
--- /dev/null
+++ b/src/illuminate/FirebirdConnector.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Firebird\Illuminate;
+
+use Illuminate\Database\Connectors\Connector;
+use Illuminate\Database\Connectors\ConnectorInterface;
+use PDO;
+
+class FirebirdConnector extends Connector implements ConnectorInterface
+{
+ /**
+ * Establish a database connection.
+ *
+ * @param array $config
+ * @return \PDO
+ */
+ public function connect(array $config): PDO
+ {
+ return $this->createConnection(
+ $this->getDsn($config),
+ $config,
+ $this->getOptions($config)
+ );
+ }
+
+ /**
+ * Create a DSN string from the configuration.
+ *
+ * @param array $config
+ * @return string
+ */
+ protected function getDsn(array $config): string
+ {
+ extract($config);
+
+ if (! isset($host) || ! isset($database)) {
+ trigger_error('Cannot connect to Firebird Database, no host or database supplied');
+ }
+
+ $dsn = "firebird:dbname={$host}";
+
+ if (isset($port)) {
+ $dsn .= "/{$port}";
+ }
+
+ $dsn .= ":{$database};";
+
+ if (isset($role)) {
+ $dsn .= "role={$role};";
+ }
+
+ if (isset($charset)) {
+ $dsn .= "charset={$charset};";
+ }
+
+ return $dsn;
+ }
+}
diff --git a/src/illuminate/FirebirdServiceProvider.php b/src/illuminate/FirebirdServiceProvider.php
new file mode 100755
index 0000000..a78889f
--- /dev/null
+++ b/src/illuminate/FirebirdServiceProvider.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Firebird\Illuminate;
+
+use Illuminate\Database\Connection;
+use Illuminate\Support\ServiceProvider;
+
+class FirebirdServiceProvider extends ServiceProvider
+{
+ /**
+ * Register any application services.
+ *
+ * @return void
+ */
+ public function register(): void
+ {
+ Connection::resolverFor('firebird', function($connection, $database, $tablePrefix, $config) {
+ return new FirebirdConnection($connection, $database, $tablePrefix, $config);
+ });
+
+ $this->app->bind('db.connector.firebird', FirebirdConnector::class);
+ }
+}
diff --git a/src/illuminate/Query/FirebirdBuilder.php b/src/illuminate/Query/FirebirdBuilder.php
new file mode 100755
index 0000000..119e804
--- /dev/null
+++ b/src/illuminate/Query/FirebirdBuilder.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Firebird\Illuminate\Query;
+
+use Illuminate\Database\Query\Builder as QueryBuilder;
+use Illuminate\Support\Collection;
+
+class FirebirdBuilder extends QueryBuilder
+{
+ /**
+ * Determine if any rows exist for the current query.
+ *
+ * @return bool
+ */
+ public function exists()
+ {
+ return parent::count() > 0;
+ }
+
+ /**
+ * Add a from stored procedure clause to the query builder.
+ *
+ * @param string $procedure
+ * @param array $values
+ * @return \Illuminate\Database\Query\Builder|static
+ */
+ public function fromProcedure(string $procedure, array $values = [])
+ {
+ $compiledProcedure = $this->grammar->compileProcedure($this, $procedure, $values);
+
+ // Remove any expressions from the values array, as they will have
+ // already been evaluated by the grammar's parameterize() function.
+ $values = array_filter($values, function($value) {
+ return ! $this->grammar->isExpression($value);
+ });
+
+ $this->fromRaw($compiledProcedure, array_values($values));
+
+ return $this;
+ }
+
+ /**
+ * Insert new records into the database.
+ *
+ * @return bool
+ */
+ public function insert(array $values)
+ {
+ // Handle multi-row inserts by looping
+ if (count($values) > 1 && is_array(reset($values))) {
+ $results = [];
+ foreach ($values as $row) {
+ $results[] = parent::insert($row);
+ }
+ return end($results);
+ }
+
+ // Single row or already formatted correctly
+ return parent::insert($values);
+ }
+ public function where($column, $operator = NULL, $value = NULL, $boolean = 'and')
+ {
+ // Not sure what this was intended to fix, but target hidden for now
+ if (! str($operator)->contains('hide', true)) {
+ return parent::where($column, $operator, $value, $boolean);
+ }
+
+ // when is search covert to upper case column and value at database level
+ $wrapped = $this->grammar->wrap($column);
+ return $boolean === 'and'
+ ? parent::whereRaw("UPPER($wrapped) LIKE UPPER(?)", [$value])
+ : parent::orWhereRaw("UPPER($wrapped) LIKE UPPER(?)", [$value]);
+ }
+
+ /**
+ * Retrieve column values from rows represented as objects.
+ *
+ * @param array $queryResult
+ * @param string $column
+ * @param string $key
+ * @return Collection
+ */
+ protected function pluckFromObjectColumn($queryResult, $column, $key)
+ {
+ $results = [];
+ $column = str_replace('"', '', $column);
+ foreach ($queryResult as $item) {
+ if (is_null($key)) {
+ foreach ($queryResult as $row) {
+ $results[] = $row->$column;
+ }
+ } else {
+ foreach ($queryResult as $row) {
+ $results[$row->$key] = $row->$column;
+ }
+ }
+ }
+
+ return new Collection($results);
+ }
+
+ /**
+ * Retrieve column values from rows represented as arrays.
+ *
+ * @param array $queryResult
+ * @param string $column
+ * @param string $key
+ * @return Collection
+ */
+ protected function pluckFromArrayColumn($queryResult, $column, $key)
+ {
+ $results = [];
+
+ if (is_null($key)) {
+ foreach ($queryResult as $row) {
+ $results[] = $row[$column];
+ }
+ } else {
+ foreach ($queryResult as $row) {
+ $results[$row[$key]] = $row[$column];
+ }
+ }
+
+ return new Collection($results);
+ }
+}
diff --git a/src/illuminate/Query/Grammars/FirebirdGrammar.php b/src/illuminate/Query/Grammars/FirebirdGrammar.php
new file mode 100755
index 0000000..efd03f4
--- /dev/null
+++ b/src/illuminate/Query/Grammars/FirebirdGrammar.php
@@ -0,0 +1,256 @@
+<?php
+
+namespace Firebird\Illuminate\Query\Grammars;
+
+use Illuminate\Database\Query\Builder;
+use Illuminate\Database\Query\Grammars\Grammar;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Str;
+
+class FirebirdGrammar extends Grammar
+{
+ /**
+ * The components that make up a select clause.
+ *
+ * @var string[]
+ */
+ protected $selectComponents = [
+ 'aggregate',
+ 'columns',
+ 'from',
+ 'joins',
+ 'wheres',
+ 'groups',
+ 'havings',
+ 'orders',
+ // 'limit', - Handled in the compileColumns() method.
+ // 'offset', - Handled in the compileColumns() method.
+ 'lock',
+ ];
+
+ /**
+ * All of the available clause operators.
+ *
+ * @var array
+ *
+ * @link https://ib-aid.com/download/docs/firebird-language-reference-2.5/fblangref25-commons-predicates.html
+ */
+ protected $operators = [
+ '=', '<', '>', '<=', '>=', '<>', '!=',
+ 'like', 'not like', 'between', 'not between',
+ 'containing', 'not containing', 'starting with', 'not starting with',
+ 'similar to', 'not similar to', 'is distinct from', 'is not distinct from',
+ ];
+
+ /**
+ * @param Builder $query
+ * @param array $columns
+ * @return string|null
+ */
+ protected function compileColumns(Builder $query, $columns)
+ {
+ // See superclass.
+ if (! is_null($query->aggregate)) {
+ return;
+ }
+
+ // In Firebird, the correct syntax for limiting and offsetting rows is
+ // "select first [num_rows] skip [start_row] * from table". Laravel does
+ // not support adding components between the "select" keyword and the
+ // column names, so compile the limit and offset components here. Note
+ // that they are commented out in the $selectComponents class variable.
+ // Reference: http://mc-computing.com/Databases/Firebird/SQL.html
+
+ $select = 'select ';
+
+ if ($query->limit) {
+ $select .= $this->compileLimit($query, $query->limit).' ';
+ }
+
+ if ($query->offset) {
+ $select .= $this->compileOffset($query, $query->offset).' ';
+ }
+
+ if ($query->distinct) {
+ $select .= 'distinct ';
+ }
+
+ return $select.$this->columnize($columns);
+ }
+
+ /**
+ * Compile a delete statement into SQL.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @return string
+ */
+ public function compileDelete(Builder $query)
+ {
+ if (isset($query->joins) ) { // || isset($query->limit)) {
+ return $this->compileDeleteWithJoinsOrLimit($query);
+ }
+
+ return parent::compileDelete($query);
+ }
+
+ /**
+ * Compile a delete statement with joins or limit into SQL.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @return string
+ */
+ protected function compileDeleteWithJoinsOrLimit(Builder $query)
+ {
+ $table = $this->wrapTable($query->from);
+ $alias = last(preg_split('/\s+as\s+/i', $query->from));
+ switch ($alias) {
+ case 'block_setting' : $field = 'block_id'; break;
+ case 'places' : $field = 'p_id'; break;
+ default : $field = $alias.'_id';
+ }
+ $selectSql = $this->compileSelect($query->select($alias.'.'.$field));
+
+ return "delete from {$table} where {$this->wrap($field)} in ({$selectSql})";
+ }
+
+ /**
+ * Compile the "limit" portions of the query.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param int $limit
+ * @return string
+ */
+ protected function compileLimit(Builder $query, $limit)
+ {
+ return 'first '.(int) $limit;
+ }
+
+ /**
+ * Compile the "offset" portions of the query.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param int $offset
+ * @return string
+ */
+ protected function compileOffset(Builder $query, $offset)
+ {
+ return 'skip '.(int) $offset;
+ }
+
+ /**
+ * Compile the random statement into SQL.
+ *
+ * @param string $seed
+ * @return string
+ */
+ public function compileRandom($seed)
+ {
+ return 'RAND()';
+ }
+
+ /**
+ * Compile an insert statement into SQL.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param array $values
+ * @return string
+ */
+ public function compileInsert(Builder $query, array $values)
+ {
+ $table = $this->wrapTable($query->from);
+
+ if (empty($values)) {
+ return "insert into {$table} default values";
+ }
+
+ if (! is_array(array_first($values))) {
+ $values = [$values];
+ }
+
+ $columns = $this->columnize(array_keys(array_first($values)));
+
+ // We need to build a list of parameter place-holders of values that are bound
+ // to the query. Each insert should have the exact same number of parameter
+ // bindings so we will loop through the record and parameterize them all.
+ $parameters = (new Collection($values))
+ ->map(fn ($record) => '('.$this->parameterize($record).')')
+ ->implode(', ');
+
+ return "insert into $table ($columns) values $parameters returning *";
+ }
+
+ /**
+ * Wrap a single string in keyword identifiers.
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function wrapValue($value)
+ {
+ // wrap reserved words in firebird
+ if ($value == 'year' or $value == 'pending' or $value == 'value') {
+ return '"'.$value.'"';
+ }
+
+ // Currently just return unwrapped
+ return $value;
+ }
+
+ /**
+ * Wrap a union subquery in parentheses.
+ *
+ * @param string $sql
+ * @return string
+ */
+ protected function wrapUnion($sql)
+ {
+ return $sql;
+ }
+
+ /**
+ * Compile a date based where clause.
+ *
+ * @param string $type
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param array $where
+ * @return string
+ */
+ protected function dateBasedWhere($type, Builder $query, $where)
+ {
+ $value = $this->parameter($where['value']);
+
+ return 'EXTRACT('.$type.' FROM '.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
+ }
+
+ /**
+ * Compile SQL statement for a stored procedure.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param string $procedure
+ * @param array $values
+ * @return string
+ */
+ public function compileProcedure(Builder $query, $procedure, ?array $values = null)
+ {
+ $procedure = $this->wrap($procedure);
+
+ return $procedure.' ('.$this->parameterize($values).')';
+ }
+
+ /**
+ * Compile an aggregated select clause.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param array $aggregate
+ * @return string
+ */
+ protected function compileAggregate(Builder $query, $aggregate)
+ {
+ // Wrap `aggregate` in double quotes to ensure the resultset returns the
+ // column name as a lowercase string. This resolves compatibility with
+ // the framework's paginator.
+ return Str::replaceLast(
+ 'as aggregate', 'as "aggregate"', parent::compileAggregate($query, $aggregate)
+ );
+ }
+}
diff --git a/src/illuminate/Query/Processors/FirebirdProcessor.php b/src/illuminate/Query/Processors/FirebirdProcessor.php
new file mode 100644
index 0000000..577ebf7
--- /dev/null
+++ b/src/illuminate/Query/Processors/FirebirdProcessor.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Firebird\Illuminate\Query\Processors;
+
+use Illuminate\Database\Query\Processors\Processor;
+use Illuminate\Database\Query\Builder;
+
+class FirebirdProcessor extends Processor
+{
+ /**
+ * Process the results of a column listing query.
+ *
+ * @param array $results
+ * @return array
+ */
+ public function processColumnListing($results)
+ {
+ return array_map(function ($result) {
+ return ((object) $result)->column_name;
+ }, $results);
+ }
+
+ /**
+ * Process an "insert get ID" query.
+ *
+ * @param \Illuminate\Database\Query\Builder $query
+ * @param string $sql
+ * @param array $values
+ * @param string|null $sequence
+ * @return int
+ */
+ public function processInsertGetId(Builder $query, $sql, $values, $sequence = null)
+ {
+ $query->getConnection()->insert($sql, $values);
+
+ $id = $query->getConnection()->getLastInsertId();
+
+ return is_numeric($id) ? (int) $id : $id;
+ }
+}
diff --git a/src/illuminate/Schema/FirebirdBuilder.php b/src/illuminate/Schema/FirebirdBuilder.php
new file mode 100755
index 0000000..7b804a4
--- /dev/null
+++ b/src/illuminate/Schema/FirebirdBuilder.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Firebird\Illuminate;
+
+use Illuminate\Database\Schema\Builder as SchemaBuilder;
+
+class FirebirdBuilder extends SchemaBuilder
+{
+ //
+}
diff --git a/src/illuminate/Schema/Grammars/FirebirdGrammar.php b/src/illuminate/Schema/Grammars/FirebirdGrammar.php
new file mode 100755
index 0000000..0b0fc2c
--- /dev/null
+++ b/src/illuminate/Schema/Grammars/FirebirdGrammar.php
@@ -0,0 +1,665 @@
+<?php
+
+namespace Firebird\Illuminate\Schema\Grammars;
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Grammars\Grammar;
+use Illuminate\Support\Fluent;
+use Firebird\IlluminateQuery\FirebirdBuilder;
+
+class FirebirdGrammar extends Grammar
+{
+ /**
+ * The possible column modifiers.
+ *
+ * @var array
+ */
+ protected $modifiers = ['Charset', 'Collate', 'Increment', 'Default', 'Nullable'];
+
+ /**
+ * The columns available as serials.
+ *
+ * @var array
+ */
+ protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger'];
+
+ /**
+ * Compile the query to determine the tables.
+ *
+ * @return string
+ */
+ public function compileTables($schema = '')
+ {
+ return 'select trim(trailing from rdb$relation_name) as "name" '
+ .'from rdb$relations '
+ .'where rdb$relation_type = 0 '
+ .'and (rdb$system_flag is null or rdb$system_flag = 0) '
+ .'order by rdb$relation_name';
+ }
+
+ /**
+ * Compile the query to determine if a table exists.
+ *
+ * @return string
+ */
+ public function compileTableExists($schema = '', $table = '')
+ {
+ return "select rdb\$relation_name from rdb\$relations where rdb\$relation_name = '$table'";
+ }
+
+ /**
+ * Compile the query to determine the views.
+ *
+ * @param string|string[]|null $schema
+ * @return string
+ */
+ public function compileViews($schema)
+ {
+ return 'select trim(trailing from rdb$relation_name) as "name", '
+ .'rdb$view_source as "definition" '
+ .'from rdb$relations '
+ .'where rdb$view_blr is not null '
+ .'and (rdb$system_flag is null or rdb$system_flag = 0)';
+ }
+
+ /**
+ * Compile the query to determine the columns.
+ *
+ * @param string $table
+ * @return string
+ */
+ public function compileColumns($schema, $table)
+ {
+ return 'select trim(trailing from rdb$field_name) as "name" '
+ .'from rdb$relation_fields '
+ .'where rdb$relation_name = '.$this->quoteString(strtoupper($table) ).' '
+ .'order by rdb$relation_name';
+ }
+
+ /**
+ * Compile the query to determine the list of columns.
+ *
+ * @param string $table
+ * @return string
+ */
+ public function compileColumnListing($table)
+ {
+ return "select trim(rdb\$field_name) as \"column_name\" from rdb\$relation_fields where rdb\$relation_name = '$table'";
+ }
+
+ /**
+ * Compile a create table command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileCreate(Blueprint $blueprint, Fluent $command)
+ {
+ if ($blueprint->temporary) {
+ throw new \LogicException('This database driver does not support temporary tables.');
+ }
+
+ $columns = implode(', ', $this->getColumns($blueprint));
+
+ $sql = 'create table '.$this->wrapTable($blueprint->getTable())." ($columns)";
+
+ return $sql;
+ }
+
+ /**
+ * Compile a drop table command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileDrop(Blueprint $blueprint, Fluent $command)
+ {
+ return 'drop table '.$this->wrapTable($blueprint->getTable());
+ }
+
+ /**
+ * Compile a drop table (if exists) command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
+ {
+ // Replace the double quotes with single quotes.
+ $table = str_replace('"', "'", $this->wrapTable($blueprint->getTable()));
+
+ return sprintf(
+ "set term #; execute block as begin if (exists(%s)) then execute statement '%s'; end set term ;#",
+ $this->compileTableExists( '', $table),
+ $this->compileDrop($blueprint, $command)
+ );
+ }
+
+ /**
+ * Compile a column addition command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileAdd(Blueprint $blueprint, Fluent $command)
+ {
+ $table = $this->wrapTable($blueprint->getTable());
+
+ $columns = $this->prefixArray('ADD', $this->getColumns($blueprint));
+
+ return 'ALTER TABLE '.$table.' '.implode(', ', $columns);
+ }
+
+ /**
+ * Compile a primary key command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compilePrimary(Blueprint $blueprint, Fluent $command)
+ {
+ $columns = $this->columnize($command->columns);
+
+ return 'ALTER TABLE '.$this->wrapTable($blueprint->getTable())." ADD PRIMARY KEY ({$columns})";
+ }
+
+ /**
+ * Compile a unique key command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileUnique(Blueprint $blueprint, Fluent $command)
+ {
+ $table = $this->wrapTable($blueprint->getTable());
+
+ $index = $this->wrap(substr($command->index, 0, 31));
+
+ $columns = $this->columnize($command->columns);
+// need to skip if the columns are already part of the primary key
+ return "ALTER TABLE {$table} ADD CONSTRAINT {$index} UNIQUE ({$columns})";
+ }
+
+ /**
+ * Compile a plain index key command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileIndex(Blueprint $blueprint, Fluent $command)
+ {
+ $columns = $this->columnize($command->columns);
+
+ $index = $this->wrap(substr($command->index, 0, 31));
+
+ $table = $this->wrapTable($blueprint->getTable());
+
+ return "CREATE INDEX {$index} ON {$table} ($columns)";
+ }
+
+ /**
+ * Compile a foreign key command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileForeign(Blueprint $blueprint, Fluent $command)
+ {
+ $table = $this->wrapTable($blueprint->getTable());
+
+ $on = $this->wrapTable($command->on);
+
+ // We need to prepare several of the elements of the foreign key definition
+ // before we can create the SQL, such as wrapping the tables and convert
+ // an array of columns to comma-delimited strings for the SQL queries.
+ $columns = $this->columnize($command->columns);
+
+ $onColumns = $this->columnize((array) $command->references);
+
+ $fkName = substr($command->index, 0, 31);
+
+ $sql = "ALTER TABLE {$table} ADD CONSTRAINT {$fkName} ";
+
+ $sql .= "FOREIGN KEY ({$columns}) REFERENCES {$on} ({$onColumns})";
+
+ // Once we have the basic foreign key creation statement constructed we can
+ // build out the syntax for what should happen on an update or delete of
+ // the affected columns, which will get something like "cascade", etc.
+ if (! is_null($command->onDelete)) {
+ $sql .= " ON DELETE {$command->onDelete}";
+ }
+
+ if (! is_null($command->onUpdate)) {
+ $sql .= " ON UPDATE {$command->onUpdate}";
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Compile a drop foreign key command.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $command
+ * @return string
+ */
+ public function compileDropForeign(Blueprint $blueprint, Fluent $command)
+ {
+ $table = $this->wrapTable($blueprint->getTable());
+
+ return "ALTER TABLE {$table} DROP CONSTRAINT {$command->index}";
+ }
+
+ /**
+ * Get the SQL for a character set column modifier.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $column
+ * @return string|null
+ */
+ protected function modifyCharset(Blueprint $blueprint, Fluent $column)
+ {
+ if (! is_null($column->charset)) {
+ return ' CHARACTER SET '.$column->charset;
+ }
+ }
+
+ /**
+ * Get the SQL for a collation column modifier.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $column
+ * @return string|null
+ */
+ protected function modifyCollate(Blueprint $blueprint, Fluent $column)
+ {
+ if (! is_null($column->collation)) {
+ return ' COLLATE '.$column->collation;
+ }
+ }
+
+ /**
+ * Get the SQL for a nullable column modifier.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $column
+ * @return string|null
+ */
+ protected function modifyNullable(Blueprint $blueprint, Fluent $column)
+ {
+ return $column->nullable ? '' : ' NOT NULL';
+ }
+
+ /**
+ * Get the SQL for a default column modifier.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $column
+ * @return string|null
+ */
+ protected function modifyDefault(Blueprint $blueprint, Fluent $column)
+ {
+ if (! is_null($column->default)) {
+ return ' DEFAULT '.$this->getDefaultValue($column->default);
+ }
+ }
+
+ /**
+ * Get the SQL for an auto-increment column modifier.
+ *
+ * @param \Illuminate\Database\Schema\Blueprint $blueprint
+ * @param \Illuminate\Support\Fluent $column
+ * @return string|null
+ */
+ protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
+ {
+ if (in_array($column->type, $this->serials) && $column->autoIncrement) {
+ return $this->hasCommand($blueprint, 'primary') || ($column->change && ! $column->primary)
+ ? ' unique'
+ : ' generated BY DEFAULT as identity primary key';
+ }
+ }
+
+ /**
+ * Create the column definition for a char type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeChar(Fluent $column)
+ {
+ return "CHAR({$column->length})";
+ }
+
+ /**
+ * Create the column definition for a string type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeString(Fluent $column)
+ {
+ return "VARCHAR({$column->length})";
+ }
+
+ /**
+ * Create the column definition for a text type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeText(Fluent $column)
+ {
+ return 'BLOB SUB_TYPE TEXT';
+ }
+
+ /**
+ * Create the column definition for a medium text type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeMediumText(Fluent $column)
+ {
+ return 'BLOB SUB_TYPE TEXT';
+ }
+
+ /**
+ * Create the column definition for a long text type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeLongText(Fluent $column)
+ {
+ return 'BLOB SUB_TYPE TEXT';
+ }
+
+ /**
+ * Create the column definition for a integer type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeInteger(Fluent $column)
+ {
+ return 'INTEGER';
+ }
+
+ /**
+ * Create the column definition for a big integer type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeBigInteger(Fluent $column)
+ {
+ return 'BIGINT';
+ }
+
+ /**
+ * Create the column definition for a medium integer type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeMediumInteger(Fluent $column)
+ {
+ return 'INTEGER';
+ }
+
+ /**
+ * Create the column definition for a tiny integer type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeTinyInteger(Fluent $column)
+ {
+ return 'SMALLINT';
+ }
+
+ /**
+ * Create the column definition for a small integer type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeSmallInteger(Fluent $column)
+ {
+ return 'SMALLINT';
+ }
+
+ /**
+ * Create the column definition for a float type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeFloat(Fluent $column)
+ {
+ return 'FLOAT';
+ }
+
+ /**
+ * Create the column definition for a double type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeDouble(Fluent $column)
+ {
+ return 'DOUBLE PRECISION';
+ }
+
+ /**
+ * Create the column definition for a decimal type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeDecimal(Fluent $column)
+ {
+ return "DECIMAL({$column->total}, {$column->places})";
+ }
+
+ /**
+ * Create the column definition for a boolean type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeBoolean(Fluent $column)
+ {
+ return 'CHAR(1)';
+ }
+
+ /**
+ * Create the column definition for an enum type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeEnum(Fluent $column)
+ {
+ $allowed = array_map(function ($a) {
+ return "'".$a."'";
+ }, $column->allowed);
+ $default = '';
+ if (! is_null($column->default)) {
+ $default .= ' DEFAULT '.$this->getDefaultValue($column->default);
+ $column->default = null;
+ }
+
+ return "VARCHAR(255) $default CHECK ({$column->name} IN (".implode(', ', $allowed).'))';
+ }
+
+ /**
+ * Create the column definition for a json type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeJson(Fluent $column)
+ {
+ return 'VARCHAR(8191)';
+ }
+
+ /**
+ * Create the column definition for a jsonb type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeJsonb(Fluent $column)
+ {
+ return 'VARCHAR(8191) CHARACTER SET OCTETS';
+ }
+
+ /**
+ * Create the column definition for a date type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeDate(Fluent $column)
+ {
+ return 'DATE';
+ }
+
+ /**
+ * Create the column definition for a date-time type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeDateTime(Fluent $column)
+ {
+ return 'TIMESTAMP';
+ }
+
+ /**
+ * Create the column definition for a date-time type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeDateTimeTz(Fluent $column)
+ {
+ // No timezone support, default to plain date time
+ return $this->typeDateTime($column);
+ }
+
+ /**
+ * Create the column definition for a time type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeTime(Fluent $column)
+ {
+ return 'TIME';
+ }
+
+ /**
+ * Create the column definition for a time type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeTimeTz(Fluent $column)
+ {
+ // No timezone support, default to plain time
+ return $this->typeTime($column);
+ }
+
+ /**
+ * Create the column definition for a timestamp type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeTimestamp(Fluent $column)
+ {
+ if ($column->useCurrent) {
+ return 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP';
+ }
+
+ return 'TIMESTAMP';
+ }
+
+ /**
+ * Create the column definition for a timestamp type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeTimestampTz(Fluent $column)
+ {
+ // No timezone support, default to plain timestamp
+ return $this->typeTimestamp($column);
+ }
+
+ /**
+ * Create the column definition for a binary type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeBinary(Fluent $column)
+ {
+ return 'BLOB SUB_TYPE BINARY';
+ }
+
+ /**
+ * Create the column definition for a uuid type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeUuid(Fluent $column)
+ {
+ return 'CHAR(36)';
+ }
+
+ /**
+ * Create the column definition for an IP address type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeIpAddress(Fluent $column)
+ {
+ return 'VARCHAR(45)';
+ }
+
+ /**
+ * Create the column definition for a MAC address type.
+ *
+ * @param \Illuminate\Support\Fluent $column
+ * @return string
+ */
+ protected function typeMacAddress(Fluent $column)
+ {
+ return 'VARCHAR(17)';
+ }
+
+ /**
+ * Wrap a single string in keyword identifiers.
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function wrapValue($value)
+ {
+ return $value;
+ }
+}