Version 1
[yaffs-website] / vendor / gabordemooij / redbean / RedBeanPHP / QueryWriter / MySQL.php
diff --git a/vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/MySQL.php b/vendor/gabordemooij/redbean/RedBeanPHP/QueryWriter/MySQL.php
new file mode 100755 (executable)
index 0000000..efffa97
--- /dev/null
@@ -0,0 +1,375 @@
+<?php
+
+namespace RedBeanPHP\QueryWriter;
+
+use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
+use RedBeanPHP\QueryWriter as QueryWriter;
+use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
+use RedBeanPHP\Adapter as Adapter;
+use RedBeanPHP\RedException\SQL as SQLException;
+
+/**
+ * RedBeanPHP MySQLWriter.
+ * This is a QueryWriter class for RedBeanPHP.
+ * This QueryWriter provides support for the MySQL/MariaDB database platform.
+ *
+ * @file    RedBeanPHP/QueryWriter/MySQL.php
+ * @author  Gabor de Mooij and the RedBeanPHP Community
+ * @license BSD/GPLv2
+ *
+ * @copyright
+ * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
+ * This source file is subject to the BSD/GPLv2 License that is bundled
+ * with this source code in the file license.txt.
+ */
+class MySQL extends AQueryWriter implements QueryWriter
+{
+       /**
+        * Data types
+        */
+       const C_DATATYPE_BOOL             = 0;
+       const C_DATATYPE_UINT32           = 2;
+       const C_DATATYPE_DOUBLE           = 3;
+       const C_DATATYPE_TEXT7            = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible
+       const C_DATATYPE_TEXT8            = 5;
+       const C_DATATYPE_TEXT16           = 6;
+       const C_DATATYPE_TEXT32           = 7;
+       const C_DATATYPE_SPECIAL_DATE     = 80;
+       const C_DATATYPE_SPECIAL_DATETIME = 81;
+       const C_DATATYPE_SPECIAL_POINT    = 90;
+       const C_DATATYPE_SPECIAL_LINESTRING = 91;
+       const C_DATATYPE_SPECIAL_POLYGON    = 92;
+       const C_DATATYPE_SPECIAL_MONEY    = 93;
+
+       const C_DATATYPE_SPECIFIED        = 99;
+
+       /**
+        * @var DBAdapter
+        */
+       protected $adapter;
+
+       /**
+        * @var string
+        */
+       protected $quoteCharacter = '`';
+
+       /**
+        * @see AQueryWriter::getKeyMapForType
+        */
+       protected function getKeyMapForType( $type )
+       {
+               $databaseName = $this->adapter->getCell('SELECT DATABASE()');
+               $table = $this->esc( $type, TRUE );
+               $keys = $this->adapter->get('
+                       SELECT
+                               information_schema.key_column_usage.constraint_name AS `name`,
+                               information_schema.key_column_usage.referenced_table_name AS `table`,
+                               information_schema.key_column_usage.column_name AS `from`,
+                               information_schema.key_column_usage.referenced_column_name AS `to`,
+                               information_schema.referential_constraints.update_rule AS `on_update`,
+                               information_schema.referential_constraints.delete_rule AS `on_delete`
+                               FROM information_schema.key_column_usage
+                               INNER JOIN information_schema.referential_constraints
+                               ON information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name
+                       WHERE
+                               information_schema.key_column_usage.table_schema = :database
+                               AND information_schema.referential_constraints.constraint_schema  = :database
+                               AND information_schema.key_column_usage.constraint_schema  = :database
+                               AND information_schema.key_column_usage.table_name = :table
+                               AND information_schema.key_column_usage.constraint_name != \'PRIMARY\'
+                               AND information_schema.key_column_usage.referenced_table_name IS NOT NULL
+               ', array( ':database' => $databaseName, ':table' => $table ) );
+               $keyInfoList = array();
+               foreach ( $keys as $k ) {
+                       $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] );
+                       $keyInfoList[$label] = array(
+                               'name'          => $k['name'],
+                               'from'          => $k['from'],
+                               'table'         => $k['table'],
+                               'to'            => $k['to'],
+                               'on_update'     => $k['on_update'],
+                               'on_delete'     => $k['on_delete']
+                       );
+               }
+               return $keyInfoList;
+       }
+
+       /**
+        * Constructor
+        *
+        * @param Adapter $adapter Database Adapter
+        */
+       public function __construct( Adapter $adapter )
+       {
+               $this->typeno_sqltype = array(
+                       MySQL::C_DATATYPE_BOOL             => ' TINYINT(1) UNSIGNED ',
+                       MySQL::C_DATATYPE_UINT32           => ' INT(11) UNSIGNED ',
+                       MySQL::C_DATATYPE_DOUBLE           => ' DOUBLE ',
+                       MySQL::C_DATATYPE_TEXT7            => ' VARCHAR(191) ',
+                       MYSQL::C_DATATYPE_TEXT8            => ' VARCHAR(255) ',
+                       MySQL::C_DATATYPE_TEXT16           => ' TEXT ',
+                       MySQL::C_DATATYPE_TEXT32           => ' LONGTEXT ',
+                       MySQL::C_DATATYPE_SPECIAL_DATE     => ' DATE ',
+                       MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
+                       MySQL::C_DATATYPE_SPECIAL_POINT    => ' POINT ',
+                       MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ',
+                       MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ',
+                       MySQL::C_DATATYPE_SPECIAL_MONEY    => ' DECIMAL(10,2) '
+               );
+
+               $this->sqltype_typeno = array();
+
+               foreach ( $this->typeno_sqltype as $k => $v ) {
+                       $this->sqltype_typeno[trim( strtolower( $v ) )] = $k;
+               }
+
+               $this->adapter = $adapter;
+
+               $this->encoding = $this->adapter->getDatabase()->getMysqlEncoding();
+       }
+
+       /**
+        * This method returns the datatype to be used for primary key IDS and
+        * foreign keys. Returns one if the data type constants.
+        *
+        * @return integer
+        */
+       public function getTypeForID()
+       {
+               return self::C_DATATYPE_UINT32;
+       }
+
+       /**
+        * @see QueryWriter::getTables
+        */
+       public function getTables()
+       {
+               return $this->adapter->getCol( 'show tables' );
+       }
+
+       /**
+        * @see QueryWriter::createTable
+        */
+       public function createTable( $table )
+       {
+               $table = $this->esc( $table );
+
+               $encoding = $this->adapter->getDatabase()->getMysqlEncoding();
+               $sql   = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci ";
+
+               $this->adapter->exec( $sql );
+       }
+
+       /**
+        * @see QueryWriter::getColumns
+        */
+       public function getColumns( $table )
+       {
+               $columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) );
+
+               $columns = array();
+               foreach ( $columnsRaw as $r ) {
+                       $columns[$r['Field']] = $r['Type'];
+               }
+
+               return $columns;
+       }
+
+       /**
+        * @see QueryWriter::scanType
+        */
+       public function scanType( $value, $flagSpecial = FALSE )
+       {
+               $this->svalue = $value;
+
+               if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL;
+               if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7;
+
+               if ( $flagSpecial ) {
+                       if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_MONEY;
+                       }
+                       if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_DATE;
+                       }
+                       if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_DATETIME;
+                       }
+                       if ( preg_match( '/^POINT\(/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_POINT;
+                       }
+                       if ( preg_match( '/^LINESTRING\(/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_LINESTRING;
+                       }
+                       if ( preg_match( '/^POLYGON\(/', $value ) ) {
+                               return MySQL::C_DATATYPE_SPECIAL_POLYGON;
+                       }
+               }
+
+               //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?).
+               if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) {
+                       return MySQL::C_DATATYPE_BOOL;
+               }
+
+               if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE;
+
+               if ( !$this->startsWithZeros( $value ) ) {
+
+                       if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) {
+                               return MySQL::C_DATATYPE_UINT32;
+                       }
+
+                       if ( is_numeric( $value ) ) {
+                               return MySQL::C_DATATYPE_DOUBLE;
+                       }
+               }
+
+               if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) {
+                       return MySQL::C_DATATYPE_TEXT7;
+               }
+
+               if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) {
+                       return MySQL::C_DATATYPE_TEXT8;
+               }
+
+               if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) {
+                       return MySQL::C_DATATYPE_TEXT16;
+               }
+
+               return MySQL::C_DATATYPE_TEXT32;
+       }
+
+       /**
+        * @see QueryWriter::code
+        */
+       public function code( $typedescription, $includeSpecials = FALSE )
+       {
+               if ( isset( $this->sqltype_typeno[$typedescription] ) ) {
+                       $r = $this->sqltype_typeno[$typedescription];
+               } else {
+                       $r = self::C_DATATYPE_SPECIFIED;
+               }
+
+               if ( $includeSpecials ) {
+                       return $r;
+               }
+
+               if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) {
+                       return self::C_DATATYPE_SPECIFIED;
+               }
+
+               return $r;
+       }
+
+       /**
+        * @see QueryWriter::addUniqueIndex
+        */
+       public function addUniqueConstraint( $type, $properties )
+       {
+               $tableNoQ = $this->esc( $type, TRUE );
+               $columns = array();
+               foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column );
+               $table = $this->esc( $type );
+               sort( $columns ); // Else we get multiple indexes due to order-effects
+               $name = 'UQ_' . sha1( implode( ',', $columns ) );
+               try {
+                       $sql = "ALTER TABLE $table
+                                                ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")";
+                       $this->adapter->exec( $sql );
+               } catch ( SQLException $e ) {
+                       //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways!
+                       return FALSE;
+               }
+               return TRUE;
+       }
+
+       /**
+        * @see QueryWriter::addIndex
+        */
+       public function addIndex( $type, $name, $property )
+       {
+               try {
+                       $table  = $this->esc( $type );
+                       $name   = preg_replace( '/\W/', '', $name );
+                       $column = $this->esc( $property );
+                       $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " );
+                       return TRUE;
+               } catch ( SQLException $e ) {
+                       return FALSE;
+               }
+       }
+
+       /**
+        * @see QueryWriter::addFK
+        * @return bool
+        */
+       public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE )
+       {
+               $table = $this->esc( $type );
+               $targetTable = $this->esc( $targetType );
+               $targetTableNoQ = $this->esc( $targetType, TRUE );
+               $field = $this->esc( $property );
+               $fieldNoQ = $this->esc( $property, TRUE );
+               $targetField = $this->esc( $targetProperty );
+               $targetFieldNoQ = $this->esc( $targetProperty, TRUE );
+               $tableNoQ = $this->esc( $type, TRUE );
+               $fieldNoQ = $this->esc( $property, TRUE );
+               if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE;
+
+               //Widen the column if it's incapable of representing a foreign key (at least INT).
+               $columns = $this->getColumns( $tableNoQ );
+               $idType = $this->getTypeForID();
+               if ( $this->code( $columns[$fieldNoQ] ) !==  $idType ) {
+                       $this->widenColumn( $type, $property, $idType );
+               }
+
+               $fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ);
+               $cName = 'c_'.$fkName;
+               try {
+                       $this->adapter->exec( "
+                               ALTER TABLE {$table}
+                               ADD CONSTRAINT $cName
+                               FOREIGN KEY $fkName ( `{$fieldNoQ}` ) REFERENCES `{$targetTableNoQ}`
+                               (`{$targetFieldNoQ}`) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';');
+               } catch ( SQLException $e ) {
+                       // Failure of fk-constraints is not a problem
+               }
+               return true;
+       }
+
+       /**
+        * @see QueryWriter::sqlStateIn
+        */
+       public function sqlStateIn( $state, $list )
+       {
+               $stateMap = array(
+                       '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
+                       '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
+                       '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
+               );
+
+               return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list );
+       }
+
+       /**
+        * @see QueryWriter::wipeAll
+        */
+       public function wipeAll()
+       {
+               $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' );
+
+               foreach ( $this->getTables() as $t ) {
+                       try {
+                               $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" );
+                       } catch ( SQLException $e ) {
+                       }
+
+                       try {
+                               $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" );
+                       } catch ( SQLException $e ) {
+                       }
+               }
+
+               $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' );
+       }
+}