From fed9e7b79870150a5caea6b94652f8e97ea1c741 Mon Sep 17 00:00:00 2001 From: Colinax Date: Sat, 20 Jun 2020 20:07:23 +0200 Subject: [PATCH] add Doctrine --- wbce/include/Doctrine/Common/ClassLoader.php | 280 ++ .../Doctrine/Common/CommonException.php | 29 + wbce/include/Doctrine/Common/Comparable.php | 46 + wbce/include/Doctrine/Common/EventArgs.php | 67 + wbce/include/Doctrine/Common/EventManager.php | 154 + .../Doctrine/Common/EventSubscriber.php | 42 + wbce/include/Doctrine/Common/Lexer.php | 37 + .../Doctrine/Common/NotifyPropertyChanged.php | 42 + .../Persistence/AbstractManagerRegistry.php | 264 ++ .../Common/Persistence/ConnectionRegistry.php | 62 + .../Persistence/Event/LifecycleEventArgs.php | 89 + .../Event/LoadClassMetadataEventArgs.php | 75 + .../Persistence/Event/ManagerEventArgs.php | 59 + .../Persistence/Event/OnClearEventArgs.php | 86 + .../Persistence/Event/PreUpdateEventArgs.php | 137 + .../Common/Persistence/ManagerRegistry.php | 111 + .../Mapping/AbstractClassMetadataFactory.php | 429 ++ .../Persistence/Mapping/ClassMetadata.php | 174 + .../Mapping/ClassMetadataFactory.php | 76 + .../Mapping/Driver/AnnotationDriver.php | 256 ++ .../Mapping/Driver/DefaultFileLocator.php | 173 + .../Persistence/Mapping/Driver/FileDriver.php | 216 + .../Mapping/Driver/FileLocator.php | 73 + .../Mapping/Driver/MappingDriver.php | 58 + .../Mapping/Driver/MappingDriverChain.php | 165 + .../Persistence/Mapping/Driver/PHPDriver.php | 70 + .../Mapping/Driver/StaticPHPDriver.php | 142 + .../Mapping/Driver/SymfonyFileLocator.php | 239 ++ .../Persistence/Mapping/MappingException.php | 98 + .../Persistence/Mapping/ReflectionService.php | 87 + .../Mapping/RuntimeReflectionService.php | 104 + .../Mapping/StaticReflectionService.php | 83 + .../Common/Persistence/ObjectManager.php | 169 + .../Common/Persistence/ObjectManagerAware.php | 51 + .../Persistence/ObjectManagerDecorator.php | 140 + .../Common/Persistence/ObjectRepository.php | 81 + .../Common/Persistence/PersistentObject.php | 250 ++ .../Doctrine/Common/Persistence/Proxy.php | 59 + .../Common/PropertyChangedListener.php | 45 + .../Common/Proxy/AbstractProxyFactory.php | 248 ++ .../Doctrine/Common/Proxy/Autoloader.php | 96 + .../Exception/InvalidArgumentException.php | 92 + .../Proxy/Exception/OutOfBoundsException.php | 42 + .../Common/Proxy/Exception/ProxyException.php | 31 + .../Exception/UnexpectedValueException.php | 88 + wbce/include/Doctrine/Common/Proxy/Proxy.php | 90 + .../Doctrine/Common/Proxy/ProxyDefinition.php | 70 + .../Doctrine/Common/Proxy/ProxyGenerator.php | 1093 +++++ .../Reflection/ClassFinderInterface.php | 37 + .../Common/Reflection/Psr0FindFile.php | 79 + .../ReflectionProviderInterface.php | 48 + .../RuntimePublicReflectionProperty.php | 76 + .../Reflection/StaticReflectionClass.php | 433 ++ .../Reflection/StaticReflectionMethod.php | 362 ++ .../Reflection/StaticReflectionParser.php | 309 ++ .../Reflection/StaticReflectionProperty.php | 178 + .../Doctrine/Common/Util/ClassUtils.php | 109 + wbce/include/Doctrine/Common/Util/Debug.php | 197 + .../Doctrine/Common/Util/Inflector.php | 31 + wbce/include/Doctrine/Common/Version.php | 53 + .../Doctrine/DBAL/Cache/ArrayStatement.php | 148 + .../Doctrine/DBAL/Cache/CacheException.php | 43 + .../Doctrine/DBAL/Cache/QueryCacheProfile.php | 146 + .../DBAL/Cache/ResultCacheStatement.php | 221 + wbce/include/Doctrine/DBAL/Configuration.php | 152 + wbce/include/Doctrine/DBAL/Connection.php | 1677 ++++++++ .../Doctrine/DBAL/ConnectionException.php | 60 + .../Connections/MasterSlaveConnection.php | 386 ++ wbce/include/Doctrine/DBAL/DBALException.php | 290 ++ wbce/include/Doctrine/DBAL/Driver.php | 75 + .../DBAL/Driver/AbstractDB2Driver.php | 60 + .../DBAL/Driver/AbstractDriverException.php | 75 + .../DBAL/Driver/AbstractMySQLDriver.php | 186 + .../DBAL/Driver/AbstractOracleDriver.php | 155 + .../DBAL/Driver/AbstractPostgreSQLDriver.php | 154 + .../DBAL/Driver/AbstractSQLAnywhereDriver.php | 149 + .../DBAL/Driver/AbstractSQLServerDriver.php | 104 + .../DBAL/Driver/AbstractSQLiteDriver.php | 117 + .../Doctrine/DBAL/Driver/Connection.php | 110 + .../Doctrine/DBAL/Driver/DriverException.php | 59 + .../Driver/DrizzlePDOMySql/Connection.php | 42 + .../DBAL/Driver/DrizzlePDOMySql/Driver.php | 78 + .../DBAL/Driver/ExceptionConverterDriver.php | 44 + .../DBAL/Driver/IBMDB2/DB2Connection.php | 181 + .../Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php | 67 + .../DBAL/Driver/IBMDB2/DB2Exception.php | 24 + .../DBAL/Driver/IBMDB2/DB2Statement.php | 362 ++ .../Doctrine/DBAL/Driver/Mysqli/Driver.php | 49 + .../DBAL/Driver/Mysqli/MysqliConnection.php | 292 ++ .../DBAL/Driver/Mysqli/MysqliException.php | 32 + .../DBAL/Driver/Mysqli/MysqliStatement.php | 413 ++ .../Doctrine/DBAL/Driver/OCI8/Driver.php | 71 + .../DBAL/Driver/OCI8/OCI8Connection.php | 231 ++ .../DBAL/Driver/OCI8/OCI8Exception.php | 35 + .../DBAL/Driver/OCI8/OCI8Statement.php | 480 +++ .../Doctrine/DBAL/Driver/PDOConnection.php | 133 + .../Doctrine/DBAL/Driver/PDOException.php | 75 + .../Doctrine/DBAL/Driver/PDOIbm/Driver.php | 83 + .../Doctrine/DBAL/Driver/PDOMySql/Driver.php | 89 + .../Doctrine/DBAL/Driver/PDOOracle/Driver.php | 78 + .../Doctrine/DBAL/Driver/PDOPgSql/Driver.php | 134 + .../Doctrine/DBAL/Driver/PDOSqlite/Driver.php | 98 + .../DBAL/Driver/PDOSqlsrv/Connection.php | 69 + .../Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php | 81 + .../DBAL/Driver/PDOSqlsrv/Statement.php | 49 + .../Doctrine/DBAL/Driver/PDOStatement.php | 170 + .../DBAL/Driver/PingableConnection.php | 39 + .../Doctrine/DBAL/Driver/ResultStatement.php | 119 + .../DBAL/Driver/SQLAnywhere/Driver.php | 104 + .../SQLAnywhere/SQLAnywhereConnection.php | 223 + .../SQLAnywhere/SQLAnywhereException.php | 94 + .../SQLAnywhere/SQLAnywhereStatement.php | 347 ++ .../Doctrine/DBAL/Driver/SQLSrv/Driver.php | 68 + .../DBAL/Driver/SQLSrv/LastInsertId.php | 50 + .../DBAL/Driver/SQLSrv/SQLSrvConnection.php | 195 + .../DBAL/Driver/SQLSrv/SQLSrvException.php | 56 + .../DBAL/Driver/SQLSrv/SQLSrvStatement.php | 386 ++ .../DBAL/Driver/ServerInfoAwareConnection.php | 44 + .../Doctrine/DBAL/Driver/Statement.php | 128 + wbce/include/Doctrine/DBAL/DriverManager.php | 427 ++ .../DBAL/Event/ConnectionEventArgs.php | 78 + .../DBAL/Event/Listeners/MysqlSessionInit.php | 80 + .../Event/Listeners/OracleSessionInit.php | 90 + .../DBAL/Event/Listeners/SQLSessionInit.php | 66 + .../SchemaAlterTableAddColumnEventArgs.php | 114 + .../SchemaAlterTableChangeColumnEventArgs.php | 114 + .../DBAL/Event/SchemaAlterTableEventArgs.php | 98 + .../SchemaAlterTableRemoveColumnEventArgs.php | 114 + .../SchemaAlterTableRenameColumnEventArgs.php | 129 + .../Event/SchemaColumnDefinitionEventArgs.php | 137 + .../SchemaCreateTableColumnEventArgs.php | 114 + .../DBAL/Event/SchemaCreateTableEventArgs.php | 128 + .../DBAL/Event/SchemaDropTableEventArgs.php | 100 + .../Doctrine/DBAL/Event/SchemaEventArgs.php | 55 + .../Event/SchemaIndexDefinitionEventArgs.php | 121 + wbce/include/Doctrine/DBAL/Events.php | 51 + .../DBAL/Exception/ConnectionException.php | 31 + .../ConstraintViolationException.php | 31 + .../DatabaseObjectExistsException.php | 35 + .../DatabaseObjectNotFoundException.php | 35 + .../DBAL/Exception/DeadlockException.php | 31 + .../DBAL/Exception/DriverException.php | 82 + ...ForeignKeyConstraintViolationException.php | 32 + .../Exception/InvalidArgumentException.php | 40 + .../Exception/InvalidFieldNameException.php | 31 + .../Exception/LockWaitTimeoutException.php | 31 + .../Exception/NonUniqueFieldNameException.php | 31 + .../NotNullConstraintViolationException.php | 32 + .../DBAL/Exception/ReadOnlyException.php | 31 + .../DBAL/Exception/RetryableException.php | 31 + .../DBAL/Exception/ServerException.php | 31 + .../DBAL/Exception/SyntaxErrorException.php | 31 + .../DBAL/Exception/TableExistsException.php | 31 + .../DBAL/Exception/TableNotFoundException.php | 31 + .../UniqueConstraintViolationException.php | 32 + .../Doctrine/DBAL/Id/TableGenerator.php | 165 + .../DBAL/Id/TableGeneratorSchemaVisitor.php | 89 + wbce/include/Doctrine/DBAL/LockMode.php | 43 + .../Doctrine/DBAL/Logging/DebugStack.php | 78 + .../Doctrine/DBAL/Logging/EchoSQLLogger.php | 56 + .../Doctrine/DBAL/Logging/LoggerChain.php | 67 + .../Doctrine/DBAL/Logging/SQLLogger.php | 51 + .../DBAL/Platforms/AbstractPlatform.php | 3573 +++++++++++++++++ .../Doctrine/DBAL/Platforms/DB2Platform.php | 881 ++++ .../DBAL/Platforms/DrizzlePlatform.php | 624 +++ .../DBAL/Platforms/Keywords/DB2Keywords.php | 441 ++ .../Platforms/Keywords/DrizzleKeywords.php | 345 ++ .../DBAL/Platforms/Keywords/KeywordList.php | 73 + .../DBAL/Platforms/Keywords/MsSQLKeywords.php | 43 + .../Platforms/Keywords/MySQL57Keywords.php | 284 ++ .../DBAL/Platforms/Keywords/MySQLKeywords.php | 287 ++ .../Platforms/Keywords/OracleKeywords.php | 161 + .../Keywords/PostgreSQL91Keywords.php | 148 + .../Keywords/PostgreSQL92Keywords.php | 50 + .../Keywords/PostgreSQL94Keywords.php | 54 + .../Platforms/Keywords/PostgreSQLKeywords.php | 135 + .../Keywords/ReservedKeywordsValidator.php | 143 + .../Keywords/SQLAnywhere11Keywords.php | 55 + .../Keywords/SQLAnywhere12Keywords.php | 64 + .../Keywords/SQLAnywhere16Keywords.php | 56 + .../Keywords/SQLAnywhereKeywords.php | 281 ++ .../Keywords/SQLServer2005Keywords.php | 56 + .../Keywords/SQLServer2008Keywords.php | 51 + .../Keywords/SQLServer2012Keywords.php | 55 + .../Platforms/Keywords/SQLServerKeywords.php | 231 ++ .../Platforms/Keywords/SQLiteKeywords.php | 168 + .../DBAL/Platforms/MySQL57Platform.php | 95 + .../Doctrine/DBAL/Platforms/MySqlPlatform.php | 1142 ++++++ .../DBAL/Platforms/OraclePlatform.php | 1179 ++++++ .../DBAL/Platforms/PostgreSQL91Platform.php | 65 + .../DBAL/Platforms/PostgreSQL92Platform.php | 88 + .../DBAL/Platforms/PostgreSQL94Platform.php | 62 + .../DBAL/Platforms/PostgreSqlPlatform.php | 1209 ++++++ .../DBAL/Platforms/SQLAnywhere11Platform.php | 47 + .../DBAL/Platforms/SQLAnywhere12Platform.php | 135 + .../DBAL/Platforms/SQLAnywhere16Platform.php | 60 + .../DBAL/Platforms/SQLAnywherePlatform.php | 1491 +++++++ .../DBAL/Platforms/SQLAzurePlatform.php | 50 + .../DBAL/Platforms/SQLServer2005Platform.php | 63 + .../DBAL/Platforms/SQLServer2008Platform.php | 119 + .../DBAL/Platforms/SQLServer2012Platform.php | 158 + .../DBAL/Platforms/SQLServerPlatform.php | 1629 ++++++++ .../DBAL/Platforms/SqlitePlatform.php | 1134 ++++++ .../Doctrine/DBAL/Portability/Connection.php | 150 + .../Doctrine/DBAL/Portability/Statement.php | 252 ++ .../Query/Expression/CompositeExpression.php | 140 + .../Query/Expression/ExpressionBuilder.php | 313 ++ .../Doctrine/DBAL/Query/QueryBuilder.php | 1356 +++++++ .../Doctrine/DBAL/Query/QueryException.php | 54 + wbce/include/Doctrine/DBAL/README.markdown | 0 wbce/include/Doctrine/DBAL/SQLParserUtils.php | 243 ++ .../Doctrine/DBAL/SQLParserUtilsException.php | 53 + .../Doctrine/DBAL/Schema/AbstractAsset.php | 227 ++ .../DBAL/Schema/AbstractSchemaManager.php | 1113 +++++ wbce/include/Doctrine/DBAL/Schema/Column.php | 487 +++ .../Doctrine/DBAL/Schema/ColumnDiff.php | 84 + .../Doctrine/DBAL/Schema/Comparator.php | 541 +++ .../Doctrine/DBAL/Schema/Constraint.php | 66 + .../Doctrine/DBAL/Schema/DB2SchemaManager.php | 218 + .../DBAL/Schema/DrizzleSchemaManager.php | 119 + .../DBAL/Schema/ForeignKeyConstraint.php | 389 ++ .../Doctrine/DBAL/Schema/Identifier.php | 48 + wbce/include/Doctrine/DBAL/Schema/Index.php | 358 ++ .../DBAL/Schema/MySqlSchemaManager.php | 252 ++ .../DBAL/Schema/OracleSchemaManager.php | 425 ++ .../DBAL/Schema/PostgreSqlSchemaManager.php | 469 +++ .../DBAL/Schema/SQLAnywhereSchemaManager.php | 243 ++ .../DBAL/Schema/SQLServerSchemaManager.php | 311 ++ wbce/include/Doctrine/DBAL/Schema/Schema.php | 512 +++ .../Doctrine/DBAL/Schema/SchemaConfig.php | 129 + .../Doctrine/DBAL/Schema/SchemaDiff.php | 204 + .../Doctrine/DBAL/Schema/SchemaException.php | 181 + .../include/Doctrine/DBAL/Schema/Sequence.php | 166 + .../DBAL/Schema/SqliteSchemaManager.php | 477 +++ .../AbstractSchemaSynchronizer.php | 65 + .../Synchronizer/SchemaSynchronizer.php | 101 + .../SingleDatabaseSynchronizer.php | 176 + wbce/include/Doctrine/DBAL/Schema/Table.php | 863 ++++ .../Doctrine/DBAL/Schema/TableDiff.php | 170 + wbce/include/Doctrine/DBAL/Schema/View.php | 53 + .../DBAL/Schema/Visitor/AbstractVisitor.php | 85 + .../Visitor/CreateSchemaSqlCollector.php | 124 + .../Schema/Visitor/DropSchemaSqlCollector.php | 126 + .../Doctrine/DBAL/Schema/Visitor/Graphviz.php | 174 + .../DBAL/Schema/Visitor/NamespaceVisitor.php | 37 + .../Schema/Visitor/RemoveNamespacedAssets.php | 94 + .../DBAL/Schema/Visitor/SchemaDiffVisitor.php | 82 + .../Doctrine/DBAL/Schema/Visitor/Visitor.php | 82 + .../DBAL/Sharding/PoolingShardConnection.php | 272 ++ .../DBAL/Sharding/PoolingShardManager.php | 134 + .../SQLAzureFederationsSynchronizer.php | 298 ++ .../SQLAzure/SQLAzureShardManager.php | 243 ++ .../SQLAzure/Schema/MultiTenantVisitor.php | 169 + .../ShardChoser/MultiTenantShardChoser.php | 39 + .../DBAL/Sharding/ShardChoser/ShardChoser.php | 41 + .../Doctrine/DBAL/Sharding/ShardManager.php | 93 + .../DBAL/Sharding/ShardingException.php | 78 + wbce/include/Doctrine/DBAL/Statement.php | 305 ++ .../Tools/Console/Command/ImportCommand.php | 130 + .../Console/Command/ReservedWordsCommand.php | 172 + .../Tools/Console/Command/RunSqlCommand.php | 89 + .../DBAL/Tools/Console/ConsoleRunner.php | 107 + .../Tools/Console/Helper/ConnectionHelper.php | 71 + .../include/Doctrine/DBAL/Types/ArrayType.php | 81 + .../Doctrine/DBAL/Types/BigIntType.php | 63 + .../Doctrine/DBAL/Types/BinaryType.php | 78 + wbce/include/Doctrine/DBAL/Types/BlobType.php | 77 + .../Doctrine/DBAL/Types/BooleanType.php | 70 + .../DBAL/Types/ConversionException.php | 114 + .../Doctrine/DBAL/Types/DateImmutableType.php | 89 + .../Doctrine/DBAL/Types/DateIntervalType.php | 70 + .../DBAL/Types/DateTimeImmutableType.php | 93 + .../Doctrine/DBAL/Types/DateTimeType.php | 84 + .../DBAL/Types/DateTimeTzImmutableType.php | 89 + .../Doctrine/DBAL/Types/DateTimeTzType.php | 97 + wbce/include/Doctrine/DBAL/Types/DateType.php | 79 + .../Doctrine/DBAL/Types/DecimalType.php | 54 + .../include/Doctrine/DBAL/Types/FloatType.php | 49 + wbce/include/Doctrine/DBAL/Types/GuidType.php | 55 + .../Doctrine/DBAL/Types/IntegerType.php | 63 + .../Doctrine/DBAL/Types/JsonArrayType.php | 62 + wbce/include/Doctrine/DBAL/Types/JsonType.php | 135 + .../Doctrine/DBAL/Types/ObjectType.php | 80 + .../Doctrine/DBAL/Types/SimpleArrayType.php | 83 + .../Doctrine/DBAL/Types/SmallIntType.php | 62 + .../Doctrine/DBAL/Types/StringType.php | 54 + wbce/include/Doctrine/DBAL/Types/TextType.php | 54 + .../Doctrine/DBAL/Types/TimeImmutableType.php | 89 + wbce/include/Doctrine/DBAL/Types/TimeType.php | 79 + wbce/include/Doctrine/DBAL/Types/Type.php | 353 ++ .../DBAL/Types/VarDateTimeImmutableType.php | 85 + .../Doctrine/DBAL/Types/VarDateTimeType.php | 56 + wbce/include/Doctrine/DBAL/Version.php | 53 + .../DBAL/VersionAwarePlatformDriver.php | 47 + wbce/include/Doctrine/index.php | 2 + wbce/include/Doctrine/readme.md | 10 + 296 files changed, 55132 insertions(+) create mode 100644 wbce/include/Doctrine/Common/ClassLoader.php create mode 100644 wbce/include/Doctrine/Common/CommonException.php create mode 100644 wbce/include/Doctrine/Common/Comparable.php create mode 100644 wbce/include/Doctrine/Common/EventArgs.php create mode 100644 wbce/include/Doctrine/Common/EventManager.php create mode 100644 wbce/include/Doctrine/Common/EventSubscriber.php create mode 100644 wbce/include/Doctrine/Common/Lexer.php create mode 100644 wbce/include/Doctrine/Common/NotifyPropertyChanged.php create mode 100644 wbce/include/Doctrine/Common/Persistence/AbstractManagerRegistry.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ConnectionRegistry.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Event/ManagerEventArgs.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Event/OnClearEventArgs.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ManagerRegistry.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadata.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/MappingException.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/ReflectionService.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ObjectManager.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ObjectManagerAware.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ObjectManagerDecorator.php create mode 100644 wbce/include/Doctrine/Common/Persistence/ObjectRepository.php create mode 100644 wbce/include/Doctrine/Common/Persistence/PersistentObject.php create mode 100644 wbce/include/Doctrine/Common/Persistence/Proxy.php create mode 100644 wbce/include/Doctrine/Common/PropertyChangedListener.php create mode 100644 wbce/include/Doctrine/Common/Proxy/AbstractProxyFactory.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Autoloader.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Exception/ProxyException.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php create mode 100644 wbce/include/Doctrine/Common/Proxy/Proxy.php create mode 100644 wbce/include/Doctrine/Common/Proxy/ProxyDefinition.php create mode 100644 wbce/include/Doctrine/Common/Proxy/ProxyGenerator.php create mode 100644 wbce/include/Doctrine/Common/Reflection/ClassFinderInterface.php create mode 100644 wbce/include/Doctrine/Common/Reflection/Psr0FindFile.php create mode 100644 wbce/include/Doctrine/Common/Reflection/ReflectionProviderInterface.php create mode 100644 wbce/include/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php create mode 100644 wbce/include/Doctrine/Common/Reflection/StaticReflectionClass.php create mode 100644 wbce/include/Doctrine/Common/Reflection/StaticReflectionMethod.php create mode 100644 wbce/include/Doctrine/Common/Reflection/StaticReflectionParser.php create mode 100644 wbce/include/Doctrine/Common/Reflection/StaticReflectionProperty.php create mode 100644 wbce/include/Doctrine/Common/Util/ClassUtils.php create mode 100644 wbce/include/Doctrine/Common/Util/Debug.php create mode 100644 wbce/include/Doctrine/Common/Util/Inflector.php create mode 100644 wbce/include/Doctrine/Common/Version.php create mode 100644 wbce/include/Doctrine/DBAL/Cache/ArrayStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Cache/CacheException.php create mode 100644 wbce/include/Doctrine/DBAL/Cache/QueryCacheProfile.php create mode 100644 wbce/include/Doctrine/DBAL/Cache/ResultCacheStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Configuration.php create mode 100644 wbce/include/Doctrine/DBAL/Connection.php create mode 100644 wbce/include/Doctrine/DBAL/ConnectionException.php create mode 100644 wbce/include/Doctrine/DBAL/Connections/MasterSlaveConnection.php create mode 100644 wbce/include/Doctrine/DBAL/DBALException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractDB2Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractDriverException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractMySQLDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractOracleDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/DriverException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/ExceptionConverterDriver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Mysqli/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/OCI8/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOIbm/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOMySql/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOOracle/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOPgSql/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOSqlite/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PDOStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/PingableConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/ResultStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLSrv/Driver.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Driver/Statement.php create mode 100644 wbce/include/Doctrine/DBAL/DriverManager.php create mode 100644 wbce/include/Doctrine/DBAL/Event/ConnectionEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php create mode 100644 wbce/include/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php create mode 100644 wbce/include/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php create mode 100644 wbce/include/Doctrine/DBAL/Events.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/ConnectionException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/ConstraintViolationException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/DeadlockException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/DriverException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/InvalidArgumentException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/InvalidFieldNameException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/LockWaitTimeoutException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/ReadOnlyException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/RetryableException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/ServerException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/SyntaxErrorException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/TableExistsException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/TableNotFoundException.php create mode 100644 wbce/include/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php create mode 100644 wbce/include/Doctrine/DBAL/Id/TableGenerator.php create mode 100644 wbce/include/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php create mode 100644 wbce/include/Doctrine/DBAL/LockMode.php create mode 100644 wbce/include/Doctrine/DBAL/Logging/DebugStack.php create mode 100644 wbce/include/Doctrine/DBAL/Logging/EchoSQLLogger.php create mode 100644 wbce/include/Doctrine/DBAL/Logging/LoggerChain.php create mode 100644 wbce/include/Doctrine/DBAL/Logging/SQLLogger.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/AbstractPlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/DB2Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/DrizzlePlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/KeywordList.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL94Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/MySQL57Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/MySqlPlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/OraclePlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLAzurePlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLServer2005Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLServer2008Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLServer2012Platform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SQLServerPlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Platforms/SqlitePlatform.php create mode 100644 wbce/include/Doctrine/DBAL/Portability/Connection.php create mode 100644 wbce/include/Doctrine/DBAL/Portability/Statement.php create mode 100644 wbce/include/Doctrine/DBAL/Query/Expression/CompositeExpression.php create mode 100644 wbce/include/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php create mode 100644 wbce/include/Doctrine/DBAL/Query/QueryBuilder.php create mode 100644 wbce/include/Doctrine/DBAL/Query/QueryException.php create mode 100644 wbce/include/Doctrine/DBAL/README.markdown create mode 100644 wbce/include/Doctrine/DBAL/SQLParserUtils.php create mode 100644 wbce/include/Doctrine/DBAL/SQLParserUtilsException.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/AbstractAsset.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/AbstractSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Column.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/ColumnDiff.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Comparator.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Constraint.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/DB2SchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/DrizzleSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/ForeignKeyConstraint.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Identifier.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Index.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/MySqlSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/OracleSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SQLServerSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Schema.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SchemaConfig.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SchemaDiff.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SchemaException.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Sequence.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/SqliteSchemaManager.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Table.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/TableDiff.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/View.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/Graphviz.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php create mode 100644 wbce/include/Doctrine/DBAL/Schema/Visitor/Visitor.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/PoolingShardConnection.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/PoolingShardManager.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/ShardManager.php create mode 100644 wbce/include/Doctrine/DBAL/Sharding/ShardingException.php create mode 100644 wbce/include/Doctrine/DBAL/Statement.php create mode 100644 wbce/include/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php create mode 100644 wbce/include/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php create mode 100644 wbce/include/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php create mode 100644 wbce/include/Doctrine/DBAL/Tools/Console/ConsoleRunner.php create mode 100644 wbce/include/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php create mode 100644 wbce/include/Doctrine/DBAL/Types/ArrayType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/BigIntType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/BinaryType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/BlobType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/BooleanType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/ConversionException.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateImmutableType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateIntervalType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateTimeImmutableType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateTimeType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateTimeTzImmutableType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateTimeTzType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DateType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/DecimalType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/FloatType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/GuidType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/IntegerType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/JsonArrayType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/JsonType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/ObjectType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/SimpleArrayType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/SmallIntType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/StringType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/TextType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/TimeImmutableType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/TimeType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/Type.php create mode 100644 wbce/include/Doctrine/DBAL/Types/VarDateTimeImmutableType.php create mode 100644 wbce/include/Doctrine/DBAL/Types/VarDateTimeType.php create mode 100644 wbce/include/Doctrine/DBAL/Version.php create mode 100644 wbce/include/Doctrine/DBAL/VersionAwarePlatformDriver.php create mode 100644 wbce/include/Doctrine/index.php create mode 100644 wbce/include/Doctrine/readme.md diff --git a/wbce/include/Doctrine/Common/ClassLoader.php b/wbce/include/Doctrine/Common/ClassLoader.php new file mode 100644 index 000000000..78eeb35d3 --- /dev/null +++ b/wbce/include/Doctrine/Common/ClassLoader.php @@ -0,0 +1,280 @@ +. + */ + +namespace Doctrine\Common; + +/** + * A ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that either loads only classes + * of a specific namespace or all namespaces and it is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @author Roman Borschel + * @since 2.0 + * + * @deprecated the ClassLoader is deprecated and will be removed in version 3.0 of doctrine/common. + */ +class ClassLoader +{ + /** + * PHP file extension. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Current namespace. + * + * @var string|null + */ + protected $namespace; + + /** + * Current include path. + * + * @var string|null + */ + protected $includePath; + + /** + * PHP namespace separator. + * + * @var string + */ + protected $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string|null $ns The namespace of the classes to load. + * @param string|null $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @param string $sep The separator to use. + * + * @return void + */ + public function setNamespaceSeparator($sep) + { + $this->namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string|null $includePath + * + * @return void + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string|null + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + * + * @return void + */ + public function register() + { + spl_autoload_register([$this, 'loadClass']); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $className The name of the class to load. + * + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) + { + if (self::typeExists($className)) { + return true; + } + + if (! $this->canLoadClass($className)) { + return false; + } + + require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return self::typeExists($className); + } + + /** + * Asks this ClassLoader whether it can potentially load the class (file) with + * the given name. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. + */ + public function canLoadClass($className) + { + if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { + return false; + } + + $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; + + if ($this->includePath !== null) { + return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); + } + + return (false !== stream_resolve_include_path($file)); + } + + /** + * Checks whether a class with a given name exists. A class "exists" if it is either + * already defined in the current request or if there is an autoloader on the SPL + * autoload stack that is a) responsible for the class in question and b) is able to + * load a class file in which the class definition resides. + * + * If the class is not already defined, each autoloader in the SPL autoload stack + * is asked whether it is able to tell if the class exists. If the autoloader is + * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. + */ + public static function classExists($className) + { + return self::typeExists($className, true); + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * + * @return ClassLoader The ClassLoader for the class or NULL if no such ClassLoader exists. + */ + public static function getClassLoader($className) + { + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader) + && ($classLoader = reset($loader)) + && $classLoader instanceof ClassLoader + && $classLoader->canLoadClass($className) + ) { + return $classLoader; + } + } + + return null; + } + + /** + * Checks whether a given type exists + * + * @param string $type + * @param bool $autoload + * + * @return bool + */ + private static function typeExists($type, $autoload = false) + { + return class_exists($type, $autoload) + || interface_exists($type, $autoload) + || trait_exists($type, $autoload); + } +} diff --git a/wbce/include/Doctrine/Common/CommonException.php b/wbce/include/Doctrine/Common/CommonException.php new file mode 100644 index 000000000..2a1a08e7e --- /dev/null +++ b/wbce/include/Doctrine/Common/CommonException.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base exception class for package Doctrine\Common. + * + * @author heinrich + */ +class CommonException extends \Exception +{ +} diff --git a/wbce/include/Doctrine/Common/Comparable.php b/wbce/include/Doctrine/Common/Comparable.php new file mode 100644 index 000000000..8cd02c9f2 --- /dev/null +++ b/wbce/include/Doctrine/Common/Comparable.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compares the current object to the passed $other. + * + * Returns 0 if they are semantically equal, 1 if the other object + * is less than the current one, or -1 if its more than the current one. + * + * This method should not check for identity using ===, only for semantical equality for example + * when two different DateTime instances point to the exact same Date + TZ. + * + * @param mixed $other + * + * @return int + */ + public function compareTo($other); +} diff --git a/wbce/include/Doctrine/Common/EventArgs.php b/wbce/include/Doctrine/Common/EventArgs.php new file mode 100644 index 000000000..75506e6ac --- /dev/null +++ b/wbce/include/Doctrine/Common/EventArgs.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\Common; + +/** + * EventArgs is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass state + * information to an event handler when an event is raised. The single empty EventArgs + * instance can be obtained through {@link getEmptyInstance}. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventArgs +{ + /** + * Single instance of EventArgs. + * + * @var EventArgs + */ + private static $_emptyEventArgsInstance; + + /** + * Gets the single, empty and immutable EventArgs instance. + * + * This instance will be used when events are dispatched without any parameter, + * like this: EventManager::dispatchEvent('eventname'); + * + * The benefit from this is that only one empty instance is instantiated and shared + * (otherwise there would be instances for every dispatched in the abovementioned form). + * + * @see EventManager::dispatchEvent + * + * @link http://msdn.microsoft.com/en-us/library/system.eventargs.aspx + * + * @return EventArgs + */ + public static function getEmptyInstance() + { + if ( ! self::$_emptyEventArgsInstance) { + self::$_emptyEventArgsInstance = new EventArgs; + } + + return self::$_emptyEventArgsInstance; + } +} diff --git a/wbce/include/Doctrine/Common/EventManager.php b/wbce/include/Doctrine/Common/EventManager.php new file mode 100644 index 000000000..0ee04a15a --- /dev/null +++ b/wbce/include/Doctrine/Common/EventManager.php @@ -0,0 +1,154 @@ +. + */ + +namespace Doctrine\Common; + +/** + * The EventManager is the central point of Doctrine's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventManager +{ + /** + * Map of registered listeners. + * => + * + * @var array + */ + private $_listeners = []; + + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. + * + * @return boolean + */ + public function dispatchEvent($eventName, EventArgs $eventArgs = null) + { + if (isset($this->_listeners[$eventName])) { + $eventArgs = $eventArgs === null ? EventArgs::getEmptyInstance() : $eventArgs; + + foreach ($this->_listeners[$eventName] as $listener) { + $listener->$eventName($eventArgs); + } + } + } + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string|null $event The name of the event. + * + * @return array The event listeners for the specified event, or all event listeners. + */ + public function getListeners($event = null) + { + return $event ? $this->_listeners[$event] : $this->_listeners; + } + + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * + * @return boolean TRUE if the specified event has any listeners, FALSE otherwise. + */ + public function hasListeners($event) + { + return isset($this->_listeners[$event]) && $this->_listeners[$event]; + } + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param object $listener The listener object. + * + * @return void + */ + public function addEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->_listeners[$event][$hash] = $listener; + } + } + + /** + * Removes an event listener from the specified events. + * + * @param string|array $events + * @param object $listener + * + * @return void + */ + public function removeEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Check if actually have this listener associated + if (isset($this->_listeners[$event][$hash])) { + unset($this->_listeners[$event][$hash]); + } + } + } + + /** + * Adds an EventSubscriber. The subscriber is asked for all the events it is + * interested in and added as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function addEventSubscriber(EventSubscriber $subscriber) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); + } + + /** + * Removes an EventSubscriber. The subscriber is asked for all the events it is + * interested in and removed as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function removeEventSubscriber(EventSubscriber $subscriber) + { + $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} diff --git a/wbce/include/Doctrine/Common/EventSubscriber.php b/wbce/include/Doctrine/Common/EventSubscriber.php new file mode 100644 index 000000000..55d0f7d0e --- /dev/null +++ b/wbce/include/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventManager, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface EventSubscriber +{ + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + public function getSubscribedEvents(); +} diff --git a/wbce/include/Doctrine/Common/Lexer.php b/wbce/include/Doctrine/Common/Lexer.php new file mode 100644 index 000000000..0aa07f856 --- /dev/null +++ b/wbce/include/Doctrine/Common/Lexer.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * Lexer moved into its own Component Doctrine\Common\Lexer. This class + * only stays for being BC. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Lexer extends AbstractLexer +{ +} diff --git a/wbce/include/Doctrine/Common/NotifyPropertyChanged.php b/wbce/include/Doctrine/Common/NotifyPropertyChanged.php new file mode 100644 index 000000000..e25e99970 --- /dev/null +++ b/wbce/include/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that provide the service of notifying listeners of + * changes to their properties. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface NotifyPropertyChanged +{ + /** + * Adds a listener that wants to be notified about property changes. + * + * @param PropertyChangedListener $listener + * + * @return void + */ + public function addPropertyChangedListener(PropertyChangedListener $listener); +} diff --git a/wbce/include/Doctrine/Common/Persistence/AbstractManagerRegistry.php b/wbce/include/Doctrine/Common/Persistence/AbstractManagerRegistry.php new file mode 100644 index 000000000..6da542236 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,264 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Abstract implementation of the ManagerRegistry contract. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +abstract class AbstractManagerRegistry implements ManagerRegistry +{ + /** + * @var string + */ + private $name; + + /** + * @var array + */ + private $connections; + + /** + * @var array + */ + private $managers; + + /** + * @var string + */ + private $defaultConnection; + + /** + * @var string + */ + private $defaultManager; + + /** + * @var string + */ + private $proxyInterfaceName; + + /** + * Constructor. + * + * @param string $name + * @param array $connections + * @param array $managers + * @param string $defaultConnection + * @param string $defaultManager + * @param string $proxyInterfaceName + */ + public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName) + { + $this->name = $name; + $this->connections = $connections; + $this->managers = $managers; + $this->defaultConnection = $defaultConnection; + $this->defaultManager = $defaultManager; + $this->proxyInterfaceName = $proxyInterfaceName; + } + + /** + * Fetches/creates the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return object The instance of the given service. + */ + abstract protected function getService($name); + + /** + * Resets the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return void + */ + abstract protected function resetService($name); + + /** + * Gets the name of the registry. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getConnection($name = null) + { + if (null === $name) { + $name = $this->defaultConnection; + } + + if (!isset($this->connections[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->connections[$name]); + } + + /** + * {@inheritdoc} + */ + public function getConnectionNames() + { + return $this->connections; + } + + /** + * {@inheritdoc} + */ + public function getConnections() + { + $connections = []; + foreach ($this->connections as $name => $id) { + $connections[$name] = $this->getService($id); + } + + return $connections; + } + + /** + * {@inheritdoc} + */ + public function getDefaultConnectionName() + { + return $this->defaultConnection; + } + + /** + * {@inheritdoc} + */ + public function getDefaultManagerName() + { + return $this->defaultManager; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException + */ + public function getManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->managers[$name]); + } + + /** + * {@inheritdoc} + */ + public function getManagerForClass($class) + { + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $proxyClass = new \ReflectionClass($class); + + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + if (! $parentClass = $proxyClass->getParentClass()) { + return null; + } + + $class = $parentClass->getName(); + } + + foreach ($this->managers as $id) { + $manager = $this->getService($id); + + if (!$manager->getMetadataFactory()->isTransient($class)) { + return $manager; + } + } + } + + /** + * {@inheritdoc} + */ + public function getManagerNames() + { + return $this->managers; + } + + /** + * {@inheritdoc} + */ + public function getManagers() + { + $dms = []; + foreach ($this->managers as $name => $id) { + $dms[$name] = $this->getService($id); + } + + return $dms; + } + + /** + * {@inheritdoc} + */ + public function getRepository($persistentObjectName, $persistentManagerName = null) + { + return $this->getManager($persistentManagerName)->getRepository($persistentObjectName); + } + + /** + * {@inheritdoc} + */ + public function resetManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + // force the creation of a new document manager + // if the current one is closed + $this->resetService($this->managers[$name]); + + return $this->getManager($name); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/ConnectionRegistry.php b/wbce/include/Doctrine/Common/Persistence/ConnectionRegistry.php new file mode 100644 index 000000000..7c25e98aa --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering connection for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ConnectionRegistry +{ + /** + * Gets the default connection name. + * + * @return string The default connection name. + */ + public function getDefaultConnectionName(); + + /** + * Gets the named connection. + * + * @param string $name The connection name (null for the default one). + * + * @return object + */ + public function getConnection($name = null); + + /** + * Gets an array of all registered connections. + * + * @return array An array of Connection instances. + */ + public function getConnections(); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public function getConnectionNames(); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php b/wbce/include/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php new file mode 100644 index 000000000..52f41c0e7 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var object + */ + private $object; + + /** + * Constructor. + * + * @param object $object + * @param ObjectManager $objectManager + */ + public function __construct($object, ObjectManager $objectManager) + { + $this->object = $object; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated entity. + * + * @deprecated + * + * @return object + */ + public function getEntity() + { + return $this->object; + } + + /** + * Retrieves the associated object. + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php b/wbce/include/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 000000000..3d8abbe23 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.2 + */ +class LoadClassMetadataEventArgs extends EventArgs +{ + /** + * @var ClassMetadata + */ + private $classMetadata; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ClassMetadata $classMetadata + * @param ObjectManager $objectManager + */ + public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) + { + $this->classMetadata = $classMetadata; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Event/ManagerEventArgs.php b/wbce/include/Doctrine/Common/Persistence/Event/ManagerEventArgs.php new file mode 100644 index 000000000..5527d4d47 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Event/ManagerEventArgs.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the preFlush event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ManagerEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + */ + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Event/OnClearEventArgs.php b/wbce/include/Doctrine/Common/Persistence/Event/OnClearEventArgs.php new file mode 100644 index 000000000..b78bad992 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the onClear event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends EventArgs +{ + /** + * @var \Doctrine\Common\Persistence\ObjectManager + */ + private $objectManager; + + /** + * @var string|null + */ + private $entityClass; + + /** + * Constructor. + * + * @param ObjectManager $objectManager The object manager. + * @param string|null $entityClass The optional entity class. + */ + public function __construct($objectManager, $entityClass = null) + { + $this->objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Returns the name of the entity class that is cleared, or null if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Returns whether this event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php b/wbce/include/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php new file mode 100644 index 000000000..f1390bc0b --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.2 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param ObjectManager $objectManager + * @param array $changeSet + */ + public function __construct($entity, ObjectManager $objectManager, array &$changeSet) + { + parent::__construct($entity, $objectManager); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieves the entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets the old value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Gets the new value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function assertValidField($field) + { + if ( ! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getObject()) + )); + } + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/ManagerRegistry.php b/wbce/include/Doctrine/Common/Persistence/ManagerRegistry.php new file mode 100644 index 000000000..fce854b62 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ManagerRegistry extends ConnectionRegistry +{ + /** + * Gets the default object manager name. + * + * @return string The default object manager name. + */ + public function getDefaultManagerName(); + + /** + * Gets a named object manager. + * + * @param string $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getManager($name = null); + + /** + * Gets an array of all registered object managers. + * + * @return \Doctrine\Common\Persistence\ObjectManager[] An array of ObjectManager instances + */ + public function getManagers(); + + /** + * Resets a named object manager. + * + * This method is useful when an object manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new object manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this object manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string|null $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function resetManager($name = null); + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered object managers. + * + * @param string $alias The alias. + * + * @return string The full namespace. + */ + public function getAliasNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public function getManagerNames(); + + /** + * Gets the ObjectRepository for an persistent object. + * + * @param string $persistentObject The name of the persistent object. + * @param string $persistentManagerName The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository($persistentObject, $persistentManagerName = null); + + /** + * Gets the object manager associated with a given class. + * + * @param string $class A persistent object class name. + * + * @return \Doctrine\Common\Persistence\ObjectManager|null + */ + public function getManagerForClass($class); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/wbce/include/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php new file mode 100644 index 000000000..cba424b06 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,429 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Util\ClassUtils; +use ReflectionException; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping informations of a class which describes how a class should be mapped + * to a relational database. + * + * This class was abstracted from the ORM ClassMetadataFactory. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractClassMetadataFactory implements ClassMetadataFactory +{ + /** + * Salt used by specific Object Manager implementation. + * + * @var string + */ + protected $cacheSalt = '$CLASSMETADATA'; + + /** + * @var \Doctrine\Common\Cache\Cache|null + */ + private $cacheDriver; + + /** + * @var ClassMetadata[] + */ + private $loadedMetadata = []; + + /** + * @var bool + */ + protected $initialized = false; + + /** + * @var ReflectionService|null + */ + private $reflectionService = null; + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param \Doctrine\Common\Cache\Cache $cacheDriver + * + * @return void + */ + public function setCacheDriver(Cache $cacheDriver = null) + { + $this->cacheDriver = $cacheDriver; + } + + /** + * Gets the cache driver used by the factory to cache ClassMetadata instances. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Returns an array of all the loaded metadata currently in memory. + * + * @return ClassMetadata[] + */ + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $driver = $this->getDriver(); + $metadata = []; + foreach ($driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + * + * @return void + */ + abstract protected function initialize(); + + /** + * Gets the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Returns the mapping driver implementation. + * + * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakes up reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initializes Reflection after ClassMetadata was constructed. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Checks whether the class metadata is an entity. + * + * This method should return false for mapped superclasses or embedded classes. + * + * @param ClassMetadata $class + * + * @return boolean + */ + abstract protected function isEntity(ClassMetadata $class); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + * + * @throws ReflectionException + * @throws MappingException + */ + public function getMetadataFor($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className, 2); + + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = ClassUtils::getRealClass($className); + } + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + $loadingException = null; + + try { + if ($this->cacheDriver) { + if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) instanceof ClassMetadata) { + $this->loadedMetadata[$realClassName] = $cached; + + $this->wakeupReflection($cached, $this->getReflectionService()); + } else { + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + $loadedClassName . $this->cacheSalt, + $this->loadedMetadata[$loadedClassName], + null + ); + } + } + } else { + $this->loadMetadata($realClassName); + } + } catch (MappingException $loadingException) { + if (! $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName)) { + throw $loadingException; + } + + $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + } + + if ($className !== $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + return $this->loadedMetadata[$className]; + } + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className) + { + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @param string $className + * @param ClassMetadata $class + * + * @return void + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Gets an array of parent classes for the given entity class. + * + * @param string $name + * + * @return array + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = []; + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ( ! $this->getDriver()->isTransient($parentClass)) { + $parentClasses[] = $parentClass; + } + } + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * Important: The class $name does not necesarily exist at this point here. + * Scenarios in a code-generation setup might have access to XML/YAML + * Mapping files without the actual PHP code existing here. That is why the + * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface + * should be used for reflection. + * + * @param string $name The name of the class for which the metadata should get loaded. + * + * @return array + */ + protected function loadMetadata($name) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $loaded = []; + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = []; + $reflService = $this->getReflectionService(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if ($this->isEntity($parent)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ($this->isEntity($class)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions + * + * Override this method to implement a fallback strategy for failed metadata loading + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null + */ + protected function onNotFoundMetadata($className) + { + return null; + } + + /** + * Actually loads the metadata from the underlying metadata. + * + * @param ClassMetadata $class + * @param ClassMetadata|null $parent + * @param bool $rootEntityFound + * @param array $nonSuperclassParents All parent class names + * that are not marked as mapped superclasses. + * + * @return void + */ + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * + * @return ClassMetadata + */ + abstract protected function newClassMetadataInstance($className); + + /** + * {@inheritDoc} + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Sets the reflectionService. + * + * @param ReflectionService $reflectionService + * + * @return void + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Gets the reflection service associated with this metadata factory. + * + * @return ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + return $this->reflectionService; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadata.php b/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadata.php new file mode 100644 index 000000000..b8445f1b9 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadata +{ + /** + * Gets the fully-qualified class name of this persistent class. + * + * @return string + */ + public function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + */ + public function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isIdentifier($fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasField($fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasAssociation($fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isSingleValuedAssociation($fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isCollectionValuedAssociation($fieldName); + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + public function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + public function getIdentifierFieldNames(); + + /** + * Returns a numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + public function getAssociationNames(); + + /** + * Returns a type name of this field. + * + * This type names can be implementation specific but should at least include the php types: + * integer, string, boolean, float/double, datetime. + * + * @param string $fieldName + * + * @return string + */ + public function getTypeOfField($fieldName); + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationTargetClass($assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association. + * + * @param string $assocName + * + * @return boolean + */ + public function isAssociationInverseSide($assocName); + + /** + * Returns the target field of the owning side of the association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationMappedByTargetField($assocName); + + /** + * Returns the identifier of this object as an array with field name as key. + * + * Has to return an empty array if no identifier isset. + * + * @param object $object + * + * @return array + */ + public function getIdentifierValues($object); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php b/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php new file mode 100644 index 000000000..bc6fb11d1 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadataFactory +{ + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return ClassMetadata[] The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + */ + public function getMetadataFor($className); + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param string $className + * + * @param ClassMetadata $class + */ + public function setMetadataFor($className, $class); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped directly or as a MappedSuperclass. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 000000000..deb82c0c9 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,256 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class AnnotationDriver implements MappingDriver +{ + /** + * The AnnotationReader. + * + * @var AnnotationReader + */ + protected $reader; + + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * The paths excluded from path where to look for mapping files. + * + * @var array + */ + protected $excludePaths = []; + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for AnnotationDriver#getAllClassNames(). + * + * @var array|null + */ + protected $classNames; + + /** + * Name of the entity annotations as keys. + * + * @var array + */ + protected $entityAnnotationClasses = []; + + /** + * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading + * docblock annotations. + * + * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. + * @param string|array|null $paths One or multiple paths where mapping classes can be found. + */ + public function __construct($reader, $paths = null) + { + $this->reader = $reader; + if ($paths) { + $this->addPaths((array) $paths); + } + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Append exclude lookup paths to metadata driver. + * + * @param array $paths + */ + public function addExcludePaths(array $paths) + { + $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); + } + + /** + * Retrieve the defined metadata lookup exclude paths. + * + * @return array + */ + public function getExcludePaths() + { + return $this->excludePaths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Returns whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * + * A class is non-transient if it is annotated with an annotation + * from the {@see AnnotationDriver::entityAnnotationClasses}. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className) + { + $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); + + foreach ($classAnnotations as $annot) { + if (isset($this->entityAnnotationClasses[get_class($annot)])) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . preg_quote($this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = $file[0]; + + if ( ! preg_match('(^phar:)i', $sourceFile)) { + $sourceFile = realpath($sourceFile); + } + + foreach ($this->excludePaths as $excludePath) { + $exclude = str_replace('\\', '/', realpath($excludePath)); + $current = str_replace('\\', '/', $sourceFile); + + if (strpos($current, $exclude) !== false) { + continue 2; + } + } + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php new file mode 100644 index 000000000..6ed7f6d0d --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,173 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +class DefaultFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * The file extension of mapping documents. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array $paths One or multiple paths where mapping documents can be found. + * @param string|null $fileExtension The file extension of mapping documents, usually prefixed with a dot. + */ + public function __construct($paths, $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string|null + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string|null $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = []; + + if ($this->paths) { + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ((array) $this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php new file mode 100644 index 000000000..f17b263c0 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,216 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Base driver for file-based metadata drivers. + * + * A file driver operates in a mode where it loads the mapping files of individual + * classes on demand. This requires the user to adhere to the convention of 1 mapping + * file per class and the file names of the mapping files must correspond to the full + * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class FileDriver implements MappingDriver +{ + /** + * @var FileLocator + */ + protected $locator; + + /** + * @var array|null + */ + protected $classCache; + + /** + * @var string|null + */ + protected $globalBasename; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array|FileLocator $locator A FileLocator or one/multiple paths + * where mapping documents can be found. + * @param string|null $fileExtension + */ + public function __construct($locator, $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array)$locator, $fileExtension); + } + } + + /** + * Sets the global basename. + * + * @param string $file + * + * @return void + */ + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + /** + * Retrieves the global basename. + * + * @return string|null + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Gets the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet. + * + * @param string $className + * + * @return array The element of schema meta data. + * + * @throws MappingException + */ + public function getElement($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + + $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + if (!isset($result[$className])) { + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension()); + } + + $this->classCache[$className] = $result[$className]; + + return $result[$className]; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return !$this->locator->fileExists($className); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (! $this->classCache) { + return (array) $this->locator->getAllClassNames($this->globalBasename); + } + + return array_merge( + array_keys($this->classCache), + (array) $this->locator->getAllClassNames($this->globalBasename) + ); + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding file driver elements. + * + * @param string $file The mapping file to load. + * + * @return array + */ + abstract protected function loadMappingFile($file); + + /** + * Initializes the class cache from all the global files. + * + * Using this feature adds a substantial performance hit to file drivers as + * more metadata has to be loaded into memory than might actually be + * necessary. This may not be relevant to scenarios where caching of + * metadata is in place, however hits very hard in scenarios where no + * caching is used. + * + * @return void + */ + protected function initialize() + { + $this->classCache = []; + if (null !== $this->globalBasename) { + foreach ($this->locator->getPaths() as $path) { + $file = $path.'/'.$this->globalBasename.$this->locator->getFileExtension(); + if (is_file($file)) { + $this->classCache = array_merge( + $this->classCache, + $this->loadMappingFile($file) + ); + } + } + } + } + + /** + * Retrieves the locator used to discover mapping files by className. + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Sets the locator used to discover mapping files by className. + * + * @param FileLocator $locator + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php new file mode 100644 index 000000000..f622856da --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +interface FileLocator +{ + /** + * Locates mapping file for the given class name. + * + * @param string $className + * + * @return string + */ + public function findMappingFile($className); + + /** + * Gets all class names that are found with this file locator. + * + * @param string $globalBasename Passed to allow excluding the basename. + * + * @return array + */ + public function getAllClassNames($globalBasename); + + /** + * Checks if a file can be found for this class name. + * + * @param string $className + * + * @return bool + */ + public function fileExists($className); + + /** + * Gets all the paths that this file locator looks for mapping files. + * + * @return array + */ + public function getPaths(); + + /** + * Gets the file extension that mapping files are suffixed with. + * + * @return string + */ + public function getFileExtension(); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php new file mode 100644 index 000000000..b5d7ec03a --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Contract for metadata drivers. + * + * @since 2.2 + * @author Jonathan H. Wage + */ +interface MappingDriver +{ + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata $metadata + * + * @return void + */ + public function loadMetadataForClass($className, ClassMetadata $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames(); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a MappedSuperclass. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php new file mode 100644 index 000000000..50d9785bc --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The DriverChain allows you to add multiple other mapping drivers for + * certain namespaces. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class MappingDriverChain implements MappingDriver +{ + /** + * The default driver. + * + * @var MappingDriver|null + */ + private $defaultDriver; + + /** + * @var array + */ + private $drivers = []; + + /** + * Gets the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param MappingDriver $driver + * + * @return void + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Adds a nested driver. + * + * @param MappingDriver $nestedDriver + * @param string $namespace + * + * @return void + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Gets the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + if (null !== $this->defaultDriver) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + return; + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classNames = []; + $driverClasses = []; + + /* @var $driver MappingDriver */ + foreach ($this->drivers AS $namespace => $driver) { + $oid = spl_object_hash($driver); + + if (!isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] AS $className) { + if (strpos($className, $namespace) === 0) { + $classNames[$className] = true; + } + } + } + + if (null !== $this->defaultDriver) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + + return array_keys($classNames); + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers AS $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + + return true; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php new file mode 100644 index 000000000..efffa6aa4 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * The PHPDriver includes php files which just populate ClassMetadataInfo + * instances with plain PHP code. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PHPDriver extends FileDriver +{ + /** + * @var ClassMetadata + */ + protected $metadata; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = null) + { + parent::__construct($locator, '.php'); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->metadata = $metadata; + + $this->loadMappingFile($this->locator->findMappingFile($className)); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $metadata = $this->metadata; + include $file; + + return [$metadata->getName() => $metadata]; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 000000000..8e37e5a7a --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,142 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The StaticPHPDriver calls a static loadMetadata() method on your entity + * classes where you can manually populate the ClassMetadata instance. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class StaticPHPDriver implements MappingDriver +{ + /** + * Paths of entity directories. + * + * @var array + */ + private $paths = []; + + /** + * Map of all class names. + * + * @var array + */ + private $classNames; + + /** + * Constructor. + * + * @param array|string $paths + */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * Adds paths. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $className::loadMetadata($metadata); + } + + /** + * {@inheritDoc} + * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->getBasename('.php') == $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return ! method_exists($className, 'loadMetadata'); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php new file mode 100644 index 000000000..4588cfec8 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,239 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The Symfony File Locator makes a simplifying assumptions compared + * to the DefaultFileLocator. By assuming paths only contain entities of a certain + * namespace the mapping files consists of the short classname only. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SymfonyFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = []; + + /** + * File extension that is searched for. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Represents PHP namespace delimiters when looking for files + * + * @var string + */ + private $nsSeparator; + + /** + * Constructor. + * + * @param array $prefixes + * @param string|null $fileExtension + * @param string $nsSeparator String which would be used when converting FQCN to filename and vice versa. Should not be empty + */ + public function __construct(array $prefixes, $fileExtension = null, $nsSeparator = '.') + { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + + if (empty($nsSeparator)) { + throw new \InvalidArgumentException('Namespace separator should not be empty'); + } + + $this->nsSeparator = (string) $nsSeparator; + } + + /** + * Adds Namespace Prefixes. + * + * @param array $prefixes + * + * @return void + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Gets Namespace Prefixes. + * + * @return array + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + // global namespace class + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return true; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', $this->nsSeparator).$this->fileExtension; + if (is_file($filename)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = []; + + if ($this->paths) { + foreach ((array) $this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->prefixes[$path])) { + + // Calculate namespace suffix for given prefix as a relative path from basepath to file path + $nsSuffix = strtr( + substr(realpath($file->getPath()), strlen(realpath($path))), + $this->nsSeparator, + '\\' + ); + + $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' .str_replace($this->nsSeparator, '\\', $fileName); + } else { + $classes[] = str_replace($this->nsSeparator, '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return $path.DIRECTORY_SEPARATOR.$defaultFileName; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', $this->nsSeparator ).$this->fileExtension; + if (is_file($filename)) { + return $filename; + } + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->fileExtension); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/MappingException.php b/wbce/include/Doctrine/Common/Persistence/Mapping/MappingException.php new file mode 100644 index 000000000..6e97485eb --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.2 + */ +class MappingException extends \Exception +{ + /** + * @param string $className + * @param array $namespaces + * + * @return self + */ + public static function classNotFoundInNamespaces($className, $namespaces) + { + return new self("The class '" . $className . "' was not found in the ". + "chain configured namespaces " . implode(", ", $namespaces)); + } + + /** + * @return self + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string|null $path + * + * @return self + */ + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } + + /** + * @param string $className + * + * @return self + */ + public static function nonExistingClass($className) + { + return new self("Class '$className' does not exist"); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/ReflectionService.php b/wbce/include/Doctrine/Common/Persistence/Mapping/ReflectionService.php new file mode 100644 index 000000000..0088ed5ee --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Very simple reflection service abstraction. + * + * This is required inside metadata layers that may require either + * static or runtime reflection. + * + * @author Benjamin Eberlei + */ +interface ReflectionService +{ + /** + * Returns an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * + * @throws \Doctrine\Common\Persistence\Mapping\MappingException + * + * @return array + */ + public function getParentClasses($class); + + /** + * Returns the shortname of a class. + * + * @param string $class + * + * @return string + */ + public function getClassShortName($class); + + /** + * @param string $class + * + * @return string + */ + public function getClassNamespace($class); + + /** + * Returns a reflection class instance or null. + * + * @param string $class + * + * @return \ReflectionClass|null + */ + public function getClass($class); + + /** + * Returns an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * + * @return \ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property); + + /** + * Checks if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * + * @return bool + */ + public function hasPublicMethod($class, $method); +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php b/wbce/include/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php new file mode 100644 index 000000000..4598d5aff --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Reflection\RuntimePublicReflectionProperty; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class RuntimeReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + if ( ! class_exists($class)) { + throw MappingException::nonExistingClass($class); + } + + return class_parents($class); + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getShortName(); + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + $reflectionProperty = new ReflectionProperty($class, $property); + + if ($reflectionProperty->isPublic()) { + $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + try { + $reflectionMethod = new ReflectionMethod($class, $method); + } catch (ReflectionException $e) { + return false; + } + + return $reflectionMethod->isPublic(); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php b/wbce/include/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php new file mode 100644 index 000000000..7d0176649 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class StaticReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + return []; + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, "\\")+1); + } + return $className; + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($className) + { + $namespace = ''; + if (strpos($className, '\\') !== false) { + $namespace = strrev(substr( strrev($className), strpos(strrev($className), '\\')+1 )); + } + return $namespace; + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + return true; + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/ObjectManager.php b/wbce/include/Doctrine/Common/Persistence/ObjectManager.php new file mode 100644 index 000000000..02208c9d5 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ObjectManager.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectManager class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectManager +{ + /** + * Finds an object by its identifier. + * + * This is just a convenient shortcut for getRepository($className)->find($id). + * + * @param string $className The class name of the object to find. + * @param mixed $id The identity of the object to find. + * + * @return object The found object. + */ + public function find($className, $id); + + /** + * Tells the ObjectManager to make an instance managed and persistent. + * + * The object will be entered into the database as a result of the flush operation. + * + * NOTE: The persist operation always considers objects that are not yet known to + * this ObjectManager as NEW. Do not pass detached objects to the persist operation. + * + * @param object $object The instance to make managed and persistent. + * + * @return void + */ + public function persist($object); + + /** + * Removes an object instance. + * + * A removed object will be removed from the database as a result of the flush operation. + * + * @param object $object The object instance to remove. + * + * @return void + */ + public function remove($object); + + /** + * Merges the state of a detached object into the persistence context + * of this ObjectManager and returns the managed copy of the object. + * The object passed to merge will not become associated/managed with this ObjectManager. + * + * @param object $object + * + * @return object + */ + public function merge($object); + + /** + * Clears the ObjectManager. All objects that are currently managed + * by this ObjectManager become detached. + * + * @param string|null $objectName if given, only objects of this type will get detached. + * + * @return void + */ + public function clear($objectName = null); + + /** + * Detaches an object from the ObjectManager, causing a managed object to + * become detached. Unflushed changes made to the object if any + * (including removal of the object), will not be synchronized to the database. + * Objects which previously referenced the detached object will continue to + * reference it. + * + * @param object $object The object to detach. + * + * @return void + */ + public function detach($object); + + /** + * Refreshes the persistent state of an object from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $object The object to refresh. + * + * @return void + */ + public function refresh($object); + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + * + * @return void + */ + public function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository($className); + + /** + * Returns the ClassMetadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)). + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + public function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory(); + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects. + * + * @param object $obj + * + * @return void + */ + public function initializeObject($obj); + + /** + * Checks if the object is part of the current UnitOfWork and therefore managed. + * + * @param object $object + * + * @return bool + */ + public function contains($object); +} diff --git a/wbce/include/Doctrine/Common/Persistence/ObjectManagerAware.php b/wbce/include/Doctrine/Common/Persistence/ObjectManagerAware.php new file mode 100644 index 000000000..9bc248a56 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Makes a Persistent Objects aware of its own object-manager. + * + * Using this interface the managing object manager and class metadata instances + * are injected into the persistent object after construction. This allows + * you to implement ActiveRecord functionality on top of the persistence-ignorance + * that Doctrine propagates. + * + * Word of Warning: This is a very powerful hook to change how you can work with your domain models. + * Using this hook will break the Single Responsibility Principle inside your Domain Objects + * and increase the coupling of database and objects. + * + * Every ObjectManager has to implement this functionality itself. + * + * @author Benjamin Eberlei + */ +interface ObjectManagerAware +{ + /** + * Injects responsible ObjectManager and the ClassMetadata into this persistent object. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @return void + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata); +} diff --git a/wbce/include/Doctrine/Common/Persistence/ObjectManagerDecorator.php b/wbce/include/Doctrine/Common/Persistence/ObjectManagerDecorator.php new file mode 100644 index 000000000..8946475db --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ObjectManagerDecorator.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Base class to simplify ObjectManager decorators + * + * @license http://opensource.org/licenses/MIT MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +abstract class ObjectManagerDecorator implements ObjectManager +{ + /** + * @var ObjectManager + */ + protected $wrapped; + + /** + * {@inheritdoc} + */ + public function find($className, $id) + { + return $this->wrapped->find($className, $id); + } + + /** + * {@inheritdoc} + */ + public function persist($object) + { + return $this->wrapped->persist($object); + } + + /** + * {@inheritdoc} + */ + public function remove($object) + { + return $this->wrapped->remove($object); + } + + /** + * {@inheritdoc} + */ + public function merge($object) + { + return $this->wrapped->merge($object); + } + + /** + * {@inheritdoc} + */ + public function clear($objectName = null) + { + return $this->wrapped->clear($objectName); + } + + /** + * {@inheritdoc} + */ + public function detach($object) + { + return $this->wrapped->detach($object); + } + + /** + * {@inheritdoc} + */ + public function refresh($object) + { + return $this->wrapped->refresh($object); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + return $this->wrapped->flush(); + } + + /** + * {@inheritdoc} + */ + public function getRepository($className) + { + return $this->wrapped->getRepository($className); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata($className) + { + return $this->wrapped->getClassMetadata($className); + } + + /** + * {@inheritdoc} + */ + public function getMetadataFactory() + { + return $this->wrapped->getMetadataFactory(); + } + + /** + * {@inheritdoc} + */ + public function initializeObject($obj) + { + return $this->wrapped->initializeObject($obj); + } + + /** + * {@inheritdoc} + */ + public function contains($object) + { + return $this->wrapped->contains($object); + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/ObjectRepository.php b/wbce/include/Doctrine/Common/Persistence/ObjectRepository.php new file mode 100644 index 000000000..fd968a3e0 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectRepository class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectRepository +{ + /** + * Finds an object by its primary key / identifier. + * + * @param mixed $id The identifier. + * + * @return object|null The object. + */ + public function find($id); + + /** + * Finds all objects in the repository. + * + * @return array The objects. + */ + public function findAll(); + + /** + * Finds objects by a set of criteria. + * + * Optionally sorting and limiting details can be passed. An implementation may throw + * an UnexpectedValueException if certain values of the sorting or limiting details are + * not supported. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array The objects. + * + * @throws \UnexpectedValueException + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + + /** + * Finds a single object by a set of criteria. + * + * @param array $criteria The criteria. + * + * @return object|null The object. + */ + public function findOneBy(array $criteria); + + /** + * Returns the class name of the object managed by the repository. + * + * @return string + */ + public function getClassName(); +} diff --git a/wbce/include/Doctrine/Common/Persistence/PersistentObject.php b/wbce/include/Doctrine/Common/Persistence/PersistentObject.php new file mode 100644 index 000000000..f90b0ccca --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/PersistentObject.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * PersistentObject base class that implements getter/setter methods for all mapped fields and associations + * by overriding __call. + * + * This class is a forward compatible implementation of the PersistentObject trait. + * + * Limitations: + * + * 1. All persistent objects have to be associated with a single ObjectManager, multiple + * ObjectManagers are not supported. You can set the ObjectManager with `PersistentObject#setObjectManager()`. + * 2. Setters and getters only work if a ClassMetadata instance was injected into the PersistentObject. + * This is either done on `postLoad` of an object or by accessing the global object manager. + * 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). + * 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. + * 5. Only the inverse side associations get autoset on the owning side as well. Setting objects on the owning side + * will not set the inverse side associations. + * + * @example + * + * PersistentObject::setObjectManager($em); + * + * class Foo extends PersistentObject + * { + * private $id; + * } + * + * $foo = new Foo(); + * $foo->getId(); // method exists through __call + * + * @author Benjamin Eberlei + */ +abstract class PersistentObject implements ObjectManagerAware +{ + /** + * @var ObjectManager|null + */ + private static $objectManager = null; + + /** + * @var ClassMetadata|null + */ + private $cm = null; + + /** + * Sets the object manager responsible for all persistent object base classes. + * + * @param ObjectManager|null $objectManager + * + * @return void + */ + static public function setObjectManager(ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager|null + */ + static public function getObjectManager() + { + return self::$objectManager; + } + + /** + * Injects the Doctrine Object Manager. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @return void + * + * @throws \RuntimeException + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) + { + if ($objectManager !== self::$objectManager) { + throw new \RuntimeException("Trying to use PersistentObject with different ObjectManager instances. " . + "Was PersistentObject::setObjectManager() called?"); + } + + $this->cm = $classMetadata; + } + + /** + * Sets a persistent fields value. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException When no persistent field exists by that name. + * @throws \InvalidArgumentException When the wrong target object type is passed to an association. + */ + private function set($field, $args) + { + if ($this->cm->hasField($field) && !$this->cm->isIdentifier($field)) { + $this->$field = $args[0]; + } else if ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass) && $args[0] !== null) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + $this->$field = $args[0]; + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + } + + /** + * Gets a persistent field value. + * + * @param string $field + * + * @return mixed + * + * @throws \BadMethodCallException When no persistent field exists by that name. + */ + private function get($field) + { + if ( $this->cm->hasField($field) || $this->cm->hasAssociation($field) ) { + return $this->$field; + } + + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + + /** + * If this is an inverse side association, completes the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + * + * @return void + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side as well, for obvious infinite recursion + // reasons this is only done when called on the inverse side. + if ($this->cm->isAssociationInverseSide($field)) { + $mappedByField = $this->cm->getAssociationMappedByTargetField($field); + $targetMetadata = self::$objectManager->getClassMetadata($targetClass); + + $setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? "add" : "set").$mappedByField; + $targetObject->$setter($this); + } + } + + /** + * Adds an object to a collection. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException + * @throws \InvalidArgumentException + */ + private function add($field, $args) + { + if ($this->cm->hasAssociation($field) && $this->cm->isCollectionValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass)) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + if (!($this->$field instanceof Collection)) { + $this->$field = new ArrayCollection($this->$field ?: []); + } + $this->$field->add($args[0]); + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("There is no method add".$field."() on ".$this->cm->getName()); + } + } + + /** + * Initializes Doctrine Metadata for this class. + * + * @return void + * + * @throws \RuntimeException + */ + private function initializeDoctrine() + { + if ($this->cm !== null) { + return; + } + + if (!self::$objectManager) { + throw new \RuntimeException("No runtime object manager set. Call PersistentObject#setObjectManager()."); + } + + $this->cm = self::$objectManager->getClassMetadata(get_class($this)); + } + + /** + * Magic methods. + * + * @param string $method + * @param array $args + * + * @return mixed + * + * @throws \BadMethodCallException + */ + public function __call($method, $args) + { + $this->initializeDoctrine(); + + $command = substr($method, 0, 3); + $field = lcfirst(substr($method, 3)); + if ($command == "set") { + $this->set($field, $args); + } else if ($command == "get") { + return $this->get($field); + } else if ($command == "add") { + $this->add($field, $args); + } else { + throw new \BadMethodCallException("There is no method ".$method." on ".$this->cm->getName()); + } + } +} diff --git a/wbce/include/Doctrine/Common/Persistence/Proxy.php b/wbce/include/Doctrine/Common/Persistence/Proxy.php new file mode 100644 index 000000000..3369eb949 --- /dev/null +++ b/wbce/include/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.2 + */ +interface Proxy +{ + /** + * Marker for Proxy class names. + * + * @var string + */ + const MARKER = '__CG__'; + + /** + * Length of the proxy marker. + * + * @var integer + */ + const MARKER_LENGTH = 6; + + /** + * Initializes this proxy if its not yet initialized. + * + * Acts as a no-op if already initialized. + * + * @return void + */ + public function __load(); + + /** + * Returns whether this proxy is initialized or not. + * + * @return bool + */ + public function __isInitialized(); +} diff --git a/wbce/include/Doctrine/Common/PropertyChangedListener.php b/wbce/include/Doctrine/Common/PropertyChangedListener.php new file mode 100644 index 000000000..1a59cd4dd --- /dev/null +++ b/wbce/include/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that are potential listeners of a NotifyPropertyChanged + * implementor. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface PropertyChangedListener +{ + /** + * Notifies the listener of a property change. + * + * @param object $sender The object on which the property changed. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property that changed. + * @param mixed $newValue The new value of the property that changed. + * + * @return void + */ + function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} diff --git a/wbce/include/Doctrine/Common/Proxy/AbstractProxyFactory.php b/wbce/include/Doctrine/Common/Proxy/AbstractProxyFactory.php new file mode 100644 index 000000000..7eb216bfc --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,248 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\OutOfBoundsException; +use Doctrine\Common\Util\ClassUtils; + +/** + * Abstract factory for proxy objects. + * + * @author Benjamin Eberlei + */ +abstract class AbstractProxyFactory +{ + /** + * Never autogenerate a proxy and rely that it was generated by some + * process before deployment. + * + * @var integer + */ + const AUTOGENERATE_NEVER = 0; + + /** + * Always generates a new proxy in every request. + * + * This is only sane during development. + * + * @var integer + */ + const AUTOGENERATE_ALWAYS = 1; + + /** + * Autogenerate the proxy class when the proxy file does not exist. + * + * This strategy causes a file exists call whenever any proxy is used the + * first time in a request. + * + * @var integer + */ + const AUTOGENERATE_FILE_NOT_EXISTS = 2; + + /** + * Generate the proxy classes using eval(). + * + * This strategy is only sane for development, and even then it gives me + * the creeps a little. + * + * @var integer + */ + const AUTOGENERATE_EVAL = 3; + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * @var \Doctrine\Common\Proxy\ProxyGenerator the proxy generator responsible for creating the proxy classes/files. + */ + private $proxyGenerator; + + /** + * @var bool Whether to automatically (re)generate proxy classes. + */ + private $autoGenerate; + + /** + * @var \Doctrine\Common\Proxy\ProxyDefinition[] + */ + private $definitions = []; + + /** + * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory + * @param bool|int $autoGenerate + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->proxyGenerator = $proxyGenerator; + $this->metadataFactory = $metadataFactory; + $this->autoGenerate = (int)$autoGenerate; + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param array $identifier + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\OutOfBoundsException + */ + public function getProxy($className, array $identifier) + { + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + $fqcn = $definition->proxyClassName; + $proxy = new $fqcn($definition->initializer, $definition->cloner); + + foreach ($definition->identifierFields as $idField) { + if (! isset($identifier[$idField])) { + throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); + } + + $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); + } + + return $proxy; + } + + /** + * Generates proxy classes for all given classes. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances) + * for which to generate proxies. + * @param string $proxyDir The target directory of the proxy classes. If not specified, the + * directory configured on the Configuration of the EntityManager used + * by this factory is used. + * @return int Number of generated proxies. + */ + public function generateProxyClasses(array $classes, $proxyDir = null) + { + $generated = 0; + + foreach ($classes as $class) { + if ($this->skipClass($class)) { + continue; + } + + $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); + + $this->proxyGenerator->generateProxyClass($class, $proxyFileName); + + $generated += 1; + } + + return $generated; + } + + /** + * Reset initialization/cloning logic for an un-initialized proxy + * + * @param \Doctrine\Common\Proxy\Proxy $proxy + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException + */ + public function resetUninitializedProxy(Proxy $proxy) + { + if ($proxy->__isInitialized()) { + throw InvalidArgumentException::unitializedProxyExpected($proxy); + } + + $className = ClassUtils::getClass($proxy); + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + + $proxy->__setInitializer($definition->initializer); + $proxy->__setCloner($definition->cloner); + + return $proxy; + } + + /** + * Get a proxy definition for the given class name. + * + * @param string $className + * + * @return ProxyDefinition + */ + private function getProxyDefinition($className) + { + $classMetadata = $this->metadataFactory->getMetadataFor($className); + $className = $classMetadata->getName(); // aliases and case sensitivity + + $this->definitions[$className] = $this->createProxyDefinition($className); + $proxyClassName = $this->definitions[$className]->proxyClassName; + + if ( ! class_exists($proxyClassName, false)) { + $fileName = $this->proxyGenerator->getProxyFileName($className); + + switch ($this->autoGenerate) { + case self::AUTOGENERATE_NEVER: + require $fileName; + break; + + case self::AUTOGENERATE_FILE_NOT_EXISTS: + if ( ! file_exists($fileName)) { + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + } + require $fileName; + break; + + case self::AUTOGENERATE_ALWAYS: + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + require $fileName; + break; + + case self::AUTOGENERATE_EVAL: + $this->proxyGenerator->generateProxyClass($classMetadata, false); + break; + } + } + + return $this->definitions[$className]; + } + + /** + * Determine if this class should be skipped during proxy generation. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $metadata + * + * @return bool + */ + abstract protected function skipClass(ClassMetadata $metadata); + + /** + * @param string $className + * + * @return ProxyDefinition + */ + abstract protected function createProxyDefinition($className); +} diff --git a/wbce/include/Doctrine/Common/Proxy/Autoloader.php b/wbce/include/Doctrine/Common/Proxy/Autoloader.php new file mode 100644 index 000000000..fae3348ee --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * + * @return string + * + * @throws InvalidArgumentException + */ + public static function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw InvalidArgumentException::notProxyClass($className, $proxyNamespace); + } + + // remove proxy namespace from class name + $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace)); + + // remove namespace separators from remaining class name + $fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace); + + return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return \Closure + * + * @throws InvalidArgumentException + */ + public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + if ( ! (null === $notFoundCallback || is_callable($notFoundCallback))) { + throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback); + } + + $autoloader = function ($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } +} diff --git a/wbce/include/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php b/wbce/include/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..dff54d9fd --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use Doctrine\Common\Persistence\Proxy; +use InvalidArgumentException as BaseInvalidArgumentException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryRequired() + { + return new self('You must configure a proxy directory. See docs for details'); + } + + /** + * @param string $className + * @param string $proxyNamespace + * + * @return self + */ + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace)); + } + + /** + * @param string $name + * + * @return self + */ + public static function invalidPlaceholder($name) + { + return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name)); + } + + /** + * @return self + */ + public static function proxyNamespaceRequired() + { + return new self('You must configure a proxy namespace'); + } + + /** + * @param Proxy $proxy + * + * @return self + */ + public static function unitializedProxyExpected(Proxy $proxy) + { + return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy))); + } + + /** + * @param mixed $callback + * + * @return self + */ + public static function invalidClassNotFoundCallback($callback) + { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type)); + } +} diff --git a/wbce/include/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php b/wbce/include/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php new file mode 100644 index 000000000..3c7415c65 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use OutOfBoundsException as BaseOutOfBoundsException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @author Fredrik Wendel + */ +class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException +{ + /** + * @param string $className + * @param string $idField + * + * @return self + */ + public static function missingPrimaryKeyValue($className, $idField) + { + return new self(sprintf("Missing value for primary key %s on %s", $idField, $className)); + } +} diff --git a/wbce/include/Doctrine/Common/Proxy/Exception/ProxyException.php b/wbce/include/Doctrine/Common/Proxy/Exception/ProxyException.php new file mode 100644 index 000000000..0d1ff1408 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +/** + * Base exception interface for proxy exceptions. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +interface ProxyException +{ +} diff --git a/wbce/include/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php b/wbce/include/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php new file mode 100644 index 000000000..ec980b593 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Proxy Unexpected Value Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @param string $proxyDirectory + * + * @return self + */ + public static function proxyDirectoryNotWritable($proxyDirectory) + { + return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory)); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * @param \Exception|null $previous + * + * @return self + */ + public static function invalidParameterTypeHint( + $className, + $methodName, + $parameterName, + \Exception $previous = null + ) { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $methodName, + $className + ), + 0, + $previous + ); + } + + /** + * @param $className + * @param $methodName + * @param \Exception|null $previous + * + * @return self + */ + public static function invalidReturnTypeHint($className, $methodName, \Exception $previous = null) + { + return new self( + sprintf( + 'The return type of method "%s" in class "%s" is invalid.', + $methodName, + $className + ), + 0, + $previous + ); + } +} diff --git a/wbce/include/Doctrine/Common/Proxy/Proxy.php b/wbce/include/Doctrine/Common/Proxy/Proxy.php new file mode 100644 index 000000000..bd2b08874 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Closure; +use Doctrine\Common\Persistence\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @author Marco Pivetta + * @since 2.4 + */ +interface Proxy extends BaseProxy +{ + /** + * Marks the proxy as initialized or not. + * + * @param boolean $initialized + * + * @return void + */ + public function __setInitialized($initialized); + + /** + * Sets the initializer callback to be used when initializing the proxy. That + * initializer should accept 3 parameters: $proxy, $method and $params. Those + * are respectively the proxy object that is being initialized, the method name + * that triggered initialization and the parameters passed to that method. + * + * @param Closure|null $initializer + * + * @return void + */ + public function __setInitializer(Closure $initializer = null); + + /** + * Retrieves the initializer callback used to initialize the proxy. + * + * @see __setInitializer + * + * @return Closure|null + */ + public function __getInitializer(); + + /** + * Sets the callback to be used when cloning the proxy. That initializer should accept + * a single parameter, which is the cloned proxy instance itself. + * + * @param Closure|null $cloner + * + * @return void + */ + public function __setCloner(Closure $cloner = null); + + /** + * Retrieves the callback to be used when cloning the proxy. + * + * @see __setCloner + * + * @return Closure|null + */ + public function __getCloner(); + + /** + * Retrieves the list of lazy loaded properties for a given proxy + * + * @return array Keys are the property names, and values are the default values + * for those properties. + */ + public function __getLazyProperties(); +} diff --git a/wbce/include/Doctrine/Common/Proxy/ProxyDefinition.php b/wbce/include/Doctrine/Common/Proxy/ProxyDefinition.php new file mode 100644 index 000000000..48b149a30 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +/** + * Definition structure how to create a proxy. + * + * @author Benjamin Eberlei + */ +class ProxyDefinition +{ + /** + * @var string + */ + public $proxyClassName; + + /** + * @var array + */ + public $identifierFields; + + /** + * @var \ReflectionProperty[] + */ + public $reflectionFields; + + /** + * @var callable + */ + public $initializer; + + /** + * @var callable + */ + public $cloner; + + /** + * @param string $proxyClassName + * @param array $identifierFields + * @param array $reflectionFields + * @param callable $initializer + * @param callable $cloner + */ + public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner) + { + $this->proxyClassName = $proxyClassName; + $this->identifierFields = $identifierFields; + $this->reflectionFields = $reflectionFields; + $this->initializer = $initializer; + $this->cloner = $cloner; + } +} + diff --git a/wbce/include/Doctrine/Common/Proxy/ProxyGenerator.php b/wbce/include/Doctrine/Common/Proxy/ProxyGenerator.php new file mode 100644 index 000000000..62f4167f3 --- /dev/null +++ b/wbce/include/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,1093 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\Common\Util\ClassUtils; + +/** + * This factory is used to generate proxy classes. + * It builds proxies from given parameters, a template and class metadata. + * + * @author Marco Pivetta + * @since 2.4 + */ +class ProxyGenerator +{ + /** + * Used to match very simple id methods that don't need + * to be decorated since the identifier is known. + */ + const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*(?::\s*\??\s*\\\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*\s*)?{\s*return\s*\$this->%s;\s*})i'; + + /** + * The namespace that contains all proxy classes. + * + * @var string + */ + private $proxyNamespace; + + /** + * The directory that contains all proxy classes. + * + * @var string + */ + private $proxyDirectory; + + /** + * Map of callables used to fill in placeholders set in the template. + * + * @var string[]|callable[] + */ + protected $placeholders = [ + 'baseProxyInterface' => Proxy::class, + 'additionalProperties' => '', + ]; + + /** + * Template used as a blueprint to generate proxies. + * + * @var string + */ + protected $proxyClassTemplate = '; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class extends \ implements \ +{ + /** + * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with + * three parameters, being respectively the proxy object to be initialized, the method that triggered the + * initialization process and an array of ordered parameters that were passed to that method. + * + * @see \Doctrine\Common\Persistence\Proxy::__setInitializer + */ + public $__initializer__; + + /** + * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object + * + * @see \Doctrine\Common\Persistence\Proxy::__setCloner + */ + public $__cloner__; + + /** + * @var boolean flag indicating if this object was already initialized + * + * @see \Doctrine\Common\Persistence\Proxy::__isInitialized + */ + public $__isInitialized__ = false; + + /** + * @var array properties to be lazy loaded, with keys being the property + * names and values being their default values + * + * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = []; + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitialized($initialized) + { + $this->__isInitialized__ = $initialized; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->__initializer__ = $initializer; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __getInitializer() + { + return $this->__initializer__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setCloner(\Closure $cloner = null) + { + $this->__cloner__ = $cloner; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific cloning logic + */ + public function __getCloner() + { + return $this->__cloner__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + * @static + */ + public function __getLazyProperties() + { + return self::$lazyPropertiesDefaults; + } + + +} +'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. + * @param string $proxyNamespace The namespace to use for the proxy classes. + * + * @throws InvalidArgumentException + */ + public function __construct($proxyDirectory, $proxyNamespace) + { + if ( ! $proxyDirectory) { + throw InvalidArgumentException::proxyDirectoryRequired(); + } + + if ( ! $proxyNamespace) { + throw InvalidArgumentException::proxyNamespaceRequired(); + } + + $this->proxyDirectory = $proxyDirectory; + $this->proxyNamespace = $proxyNamespace; + } + + /** + * Sets a placeholder to be replaced in the template. + * + * @param string $name + * @param string|callable $placeholder + * + * @throws InvalidArgumentException + */ + public function setPlaceholder($name, $placeholder) + { + if ( ! is_string($placeholder) && ! is_callable($placeholder)) { + throw InvalidArgumentException::invalidPlaceholder($name); + } + + $this->placeholders[$name] = $placeholder; + } + + /** + * Sets the base template used to create proxy classes. + * + * @param string $proxyClassTemplate + */ + public function setProxyClassTemplate($proxyClassTemplate) + { + $this->proxyClassTemplate = (string) $proxyClassTemplate; + } + + /** + * Generates a proxy class file. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class. + * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. + * + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = false) + { + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = []; + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = isset($this->placeholders[$name]) + ? $this->placeholders[$name] + : [$this, 'generate' . $name]; + } + + foreach ($placeholders as & $placeholder) { + if (is_callable($placeholder)) { + $placeholder = call_user_func($placeholder, $class); + } + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + + if ( ! $fileName) { + $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); + + if ( ! class_exists($proxyClassName)) { + eval(substr($proxyCode, 5)); + } + + return; + } + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + if ( ! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + @chmod($tmpFileName, 0664); + rename($tmpFileName, $fileName); + } + + /** + * Generates the proxy short class name to be used in the template. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateProxyShortClassName(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[0]); + } + + /** + * Generates the proxy namespace. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateNamespace(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[1]); + } + + /** + * Generates the original class name. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateClassName(ClassMetadata $class) + { + return ltrim($class->getName(), '\\'); + } + + /** + * Generates the array representation of lazy loaded public properties and their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateLazyPropertiesDefaults(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $values = []; + + foreach ($lazyPublicProperties as $key => $value) { + $values[] = var_export($key, true) . ' => ' . var_export($value, true); + } + + return implode(', ', $values); + } + + /** + * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateConstructorImpl(ClassMetadata $class) + { + $constructorImpl = <<<'EOT' + /** + * @param \Closure $initializer + * @param \Closure $cloner + */ + public function __construct($initializer = null, $cloner = null) + { + +EOT; + $toUnset = []; + + foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) { + $toUnset[] = '$this->' . $lazyPublicProperty; + } + + $constructorImpl .= (empty($toUnset) ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") + . <<<'EOT' + + $this->__initializer__ = $initializer; + $this->__cloner__ = $cloner; + } +EOT; + + return $constructorImpl; + } + + /** + * Generates the magic getter invoked when lazy loaded public properties are requested. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicGet(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $reflectionClass = $class->getReflectionClass(); + $hasParentGet = false; + $returnReference = ''; + $inheritDoc = ''; + + if ($reflectionClass->hasMethod('__get')) { + $hasParentGet = true; + $inheritDoc = '{@inheritDoc}'; + + if ($reflectionClass->getMethod('__get')->returnsReference()) { + $returnReference = '& '; + } + } + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $magicGet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return $this->$name; + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return parent::__get($name); + +EOT; + } else { + $magicGet .= <<<'EOT' + trigger_error(sprintf('Undefined property: %s::$%s', __CLASS__, $name), E_USER_NOTICE); + +EOT; + } + + $magicGet .= " }"; + + return $magicGet; + } + + /** + * Generates the magic setter (currently unused). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicSet(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $hasParentSet = $class->getReflectionClass()->hasMethod('__set'); + + if (empty($lazyPublicProperties) && ! $hasParentSet) { + return ''; + } + + $inheritDoc = $hasParentSet ? '{@inheritDoc}' : ''; + $magicSet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + $this->$name = $value; + + return; + } + + +EOT; + } + + if ($hasParentSet) { + $magicSet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + return parent::__set($name, $value); +EOT; + } else { + $magicSet .= " \$this->\$name = \$value;"; + } + + $magicSet .= "\n }"; + + return $magicSet; + } + + /** + * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicIsset(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); + + if (empty($lazyPublicProperties) && ! $hasParentIsset) { + return ''; + } + + $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; + $magicIsset = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return isset($this->$name); + } + + +EOT; + } + + if ($hasParentIsset) { + $magicIsset .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return parent::__isset($name); + +EOT; + } else { + $magicIsset .= " return false;"; + } + + return $magicIsset . "\n }"; + } + + /** + * Generates implementation for the `__sleep` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateSleepImpl(ClassMetadata $class) + { + $hasParentSleep = $class->getReflectionClass()->hasMethod('__sleep'); + $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; + $sleepImpl = <<__isInitialized__) { + $properties = array_diff($properties, array_keys($this->__getLazyProperties())); + } + + return $properties; + } +EOT; + } + + $allProperties = ['__isInitialized__']; + + /* @var $prop \ReflectionProperty */ + foreach ($class->getReflectionClass()->getProperties() as $prop) { + if ($prop->isStatic()) { + continue; + } + + $allProperties[] = $prop->isPrivate() + ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() + : $prop->getName(); + } + + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $protectedProperties = array_diff($allProperties, $lazyPublicProperties); + + foreach ($allProperties as &$property) { + $property = var_export($property, true); + } + + foreach ($protectedProperties as &$property) { + $property = var_export($property, true); + } + + $allProperties = implode(', ', $allProperties); + $protectedProperties = implode(', ', $protectedProperties); + + return $sleepImpl . <<__isInitialized__) { + return [$allProperties]; + } + + return [$protectedProperties]; + } +EOT; + } + + /** + * Generates implementation for the `__wakeup` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateWakeupImpl(ClassMetadata $class) + { + $unsetPublicProperties = []; + $hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup'); + + foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) { + $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; + } + + $shortName = $this->generateProxyShortClassName($class); + $inheritDoc = $hasWakeup ? '{@inheritDoc}' : ''; + $wakeupImpl = <<__isInitialized__) { + \$this->__initializer__ = function ($shortName \$proxy) { + \$proxy->__setInitializer(null); + \$proxy->__setCloner(null); + + \$existingProperties = get_object_vars(\$proxy); + + foreach (\$proxy->__getLazyProperties() as \$property => \$defaultValue) { + if ( ! array_key_exists(\$property, \$existingProperties)) { + \$proxy->\$property = \$defaultValue; + } + } + }; + +EOT; + + if ( ! empty($unsetPublicProperties)) { + $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ");"; + } + + $wakeupImpl .= "\n }"; + + if ($hasWakeup) { + $wakeupImpl .= "\n parent::__wakeup();"; + } + + $wakeupImpl .= "\n }"; + + return $wakeupImpl; + } + + /** + * Generates implementation for the `__clone` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateCloneImpl(ClassMetadata $class) + { + $hasParentClone = $class->getReflectionClass()->hasMethod('__clone'); + $inheritDoc = $hasParentClone ? '{@inheritDoc}' : ''; + $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : ''; + + return <<__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []); +$callParentClone } +EOT; + } + + /** + * Generates decorated methods by picking those available in the parent class. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMethods(ClassMetadata $class) + { + $methods = ''; + $methodNames = []; + $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC); + $skippedMethods = [ + '__sleep' => true, + '__clone' => true, + '__wakeup' => true, + '__get' => true, + '__set' => true, + '__isset' => true, + ]; + + foreach ($reflectionMethods as $method) { + $name = $method->getName(); + + if ( + $method->isConstructor() || + isset($skippedMethods[strtolower($name)]) || + isset($methodNames[$name]) || + $method->isFinal() || + $method->isStatic() || + ( ! $method->isPublic()) + ) { + continue; + } + + $methodNames[$name] = true; + $methods .= "\n /**\n" + . " * {@inheritDoc}\n" + . " */\n" + . ' public function '; + + if ($method->returnsReference()) { + $methods .= '&'; + } + + $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')'; + $methods .= $this->getMethodReturnType($method); + $methods .= "\n" . ' {' . "\n"; + + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($name, 3)); + $fieldType = $class->getTypeOfField($identifier); + $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' '; + $methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : ''; + $methods .= $cast . ' parent::' . $method->getName() . "();\n"; + $methods .= ' }' . "\n\n"; + } + + $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); + $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); + + $methods .= "\n \$this->__initializer__ " + . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true) + . ", [" . $invokeParamsString . "]);" + . "\n\n " + . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') + . "parent::" . $name . '(' . $callParamsString . ');' + . "\n" . ' }' . "\n"; + } + + return $methods; + } + + /** + * Generates the Proxy file name. + * + * @param string $className + * @param string $baseDirectory Optional base directory for proxy file name generation. + * If not specified, the directory configured on the Configuration of the + * EntityManager will be used by this factory. + * + * @return string + */ + public function getProxyFileName($className, $baseDirectory = null) + { + $baseDirectory = $baseDirectory ?: $this->proxyDirectory; + + return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER + . str_replace('\\', '', $className) . '.php'; + } + + /** + * Checks if the method is a short identifier getter. + * + * What does this mean? For proxy objects the identifier is already known, + * however accessing the getter for this identifier usually triggers the + * lazy loading, leading to a query that may not be necessary if only the + * ID is interesting for the userland code (for example in views that + * generate links to the entity, but do not display anything else). + * + * @param \ReflectionMethod $method + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return boolean + */ + private function isShortIdentifierGetter($method, ClassMetadata $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $startLine = $method->getStartLine(); + $endLine = $method->getEndLine(); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 + && substr($method->getName(), 0, 3) == 'get' + && in_array($identifier, $class->getIdentifier(), true) + && $class->hasField($identifier) + && (($endLine - $startLine) <= 4) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + + return false; + } + + /** + * Generates the list of public properties to be lazy loaded, with their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return mixed[] + */ + private function getLazyLoadedPublicProperties(ClassMetadata $class) + { + $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); + $properties = []; + + foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + + if (($class->hasField($name) || $class->hasAssociation($name)) && ! $class->isIdentifier($name)) { + $properties[$name] = $defaultProperties[$name]; + } + } + + return $properties; + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter[] $parameters + * + * @return string + */ + private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters) + { + $parameterDefinitions = []; + + /* @var $param \ReflectionParameter */ + foreach ($parameters as $param) { + $parameterDefinition = ''; + + if ($parameterType = $this->getParameterType($class, $method, $param)) { + $parameterDefinition .= $parameterType . ' '; + } + + if ($param->isPassedByReference()) { + $parameterDefinition .= '&'; + } + + if (method_exists($param, 'isVariadic') && $param->isVariadic()) { + $parameterDefinition .= '...'; + } + + $parameters[] = '$' . $param->getName(); + $parameterDefinition .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true); + } + + $parameterDefinitions[] = $parameterDefinition; + } + + return implode(', ', $parameterDefinitions); + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter $parameter + * + * @return string|null + */ + private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter) + { + if (method_exists($parameter, 'hasType')) { + if ( ! $parameter->hasType()) { + return ''; + } + + return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter); + } + + // For PHP 5.x, we need to pick the type hint in the old way (to be removed for PHP 7.0+) + if ($parameter->isArray()) { + return 'array'; + } + + if ($parameter->isCallable()) { + return 'callable'; + } + + try { + $parameterClass = $parameter->getClass(); + + if ($parameterClass) { + return '\\' . $parameterClass->getName(); + } + } catch (\ReflectionException $previous) { + throw UnexpectedValueException::invalidParameterTypeHint( + $class->getName(), + $method->getName(), + $parameter->getName(), + $previous + ); + } + + return null; + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForInvoke(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + return '$' . $parameter->getName(); + }, + $parameters + ); + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForParentCall(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + $name = ''; + + if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) { + $name .= '...'; + } + + $name .= '$' . $parameter->getName(); + + return $name; + }, + $parameters + ); + } + + /** + * @Param \ReflectionMethod $method + * + * @return string + */ + private function getMethodReturnType(\ReflectionMethod $method) + { + if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) { + return ''; + } + + return ': ' . $this->formatType($method->getReturnType(), $method); + } + + /** + * @param \ReflectionMethod $method + * + * @return bool + */ + private function shouldProxiedMethodReturn(\ReflectionMethod $method) + { + if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) { + return true; + } + + return 'void' !== strtolower($this->formatType($method->getReturnType(), $method)); + } + + /** + * @param \ReflectionType $type + * @param \ReflectionMethod $method + * @param \ReflectionParameter|null $parameter + * + * @return string + */ + private function formatType( + \ReflectionType $type, + \ReflectionMethod $method, + \ReflectionParameter $parameter = null + ) { + $name = method_exists($type, 'getName') ? $type->getName() : (string) $type; + $nameLower = strtolower($name); + + if ('self' === $nameLower) { + $name = $method->getDeclaringClass()->getName(); + } + + if ('parent' === $nameLower) { + $name = $method->getDeclaringClass()->getParentClass()->getName(); + } + + if ( ! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name)) { + if (null !== $parameter) { + throw UnexpectedValueException::invalidParameterTypeHint( + $method->getDeclaringClass()->getName(), + $method->getName(), + $parameter->getName() + ); + } + + throw UnexpectedValueException::invalidReturnTypeHint( + $method->getDeclaringClass()->getName(), + $method->getName() + ); + } + + if ( ! $type->isBuiltin()) { + $name = '\\' . $name; + } + + if ($type->allowsNull() + && (null === $parameter || ! $parameter->isDefaultValueAvailable() || null !== $parameter->getDefaultValue()) + ) { + $name = '?' . $name; + } + + return $name; + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/ClassFinderInterface.php b/wbce/include/Doctrine/Common/Reflection/ClassFinderInterface.php new file mode 100644 index 000000000..639fd69a6 --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +interface ClassFinderInterface +{ + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return string|null The name of the class or NULL if not found. + */ + public function findFile($class); +} diff --git a/wbce/include/Doctrine/Common/Reflection/Psr0FindFile.php b/wbce/include/Doctrine/Common/Reflection/Psr0FindFile.php new file mode 100644 index 000000000..418bb0f7b --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/Psr0FindFile.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +class Psr0FindFile implements ClassFinderInterface +{ + /** + * The PSR-0 prefixes. + * + * @var array + */ + protected $prefixes; + + /** + * @param array $prefixes An array of prefixes. Each key is a PHP namespace and each value is + * a list of directories. + */ + public function __construct($prefixes) + { + $this->prefixes = $prefixes; + } + + /** + * {@inheritDoc} + */ + public function findFile($class) + { + $lastNsPos = strrpos($class, '\\'); + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $lastNsPos) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $lastNsPos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $lastNsPos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + return null; + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/ReflectionProviderInterface.php b/wbce/include/Doctrine/Common/Reflection/ReflectionProviderInterface.php new file mode 100644 index 000000000..3d970eeed --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +interface ReflectionProviderInterface +{ + /** + * Gets the ReflectionClass equivalent for this class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Gets the ReflectionMethod equivalent for this class. + * + * @param string $name + * + * @return \ReflectionMethod + */ + public function getReflectionMethod($name); + + /** + * Gets the ReflectionProperty equivalent for this class. + * + * @param string $name + * + * @return \ReflectionProperty + */ + public function getReflectionProperty($name); +} diff --git a/wbce/include/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php b/wbce/include/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php new file mode 100644 index 000000000..4e8ef5303 --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Proxy\Proxy; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Public Property - special overrides for public properties. + * + * @author Marco Pivetta + * @since 2.4 + */ +class RuntimePublicReflectionProperty extends ReflectionProperty +{ + /** + * {@inheritDoc} + * + * Checks is the value actually exist before fetching it. + * This is to avoid calling `__get` on the provided $object if it + * is a {@see \Doctrine\Common\Proxy\Proxy}. + */ + public function getValue($object = null) + { + $name = $this->getName(); + + if ($object instanceof Proxy && ! $object->__isInitialized()) { + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + $val = isset($object->$name) ? $object->$name : null; + $object->__setInitializer($originalInitializer); + + return $val; + } + + return isset($object->$name) ? parent::getValue($object) : null; + } + + /** + * {@inheritDoc} + * + * Avoids triggering lazy loading via `__set` if the provided object + * is a {@see \Doctrine\Common\Proxy\Proxy}. + * @link https://bugs.php.net/bug.php?id=63463 + */ + public function setValue($object, $value = null) + { + if ( ! ($object instanceof Proxy && ! $object->__isInitialized())) { + parent::setValue($object, $value); + + return; + } + + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + parent::setValue($object, $value); + $object->__setInitializer($originalInitializer); + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/StaticReflectionClass.php b/wbce/include/Doctrine/Common/Reflection/StaticReflectionClass.php new file mode 100644 index 000000000..2d0f5b007 --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionClass; +use ReflectionException; + +class StaticReflectionClass extends ReflectionClass +{ + /** + * The static reflection parser object. + * + * @var StaticReflectionParser + */ + private $staticReflectionParser; + + /** + * @param StaticReflectionParser $staticReflectionParser + */ + public function __construct(StaticReflectionParser $staticReflectionParser) + { + $this->staticReflectionParser = $staticReflectionParser; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + /** + * {@inheritDoc} + */ + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + /** + * {@inheritDoc} + */ + public static function export($argument, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstants() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getDefaultProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaceNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaces() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getMethods($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParentClass() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getProperties($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticPropertyValue($name, $default = '') + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitAliases() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraits() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasMethod($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasProperty($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function implementsInterface($interface) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCloneable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstance($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstantiable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInterface() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isIterateable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isSubclassOf($class) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isTrait() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstance($args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceArgs(array $args = []) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceWithoutConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setStaticPropertyValue($name, $value) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/StaticReflectionMethod.php b/wbce/include/Doctrine/Common/Reflection/StaticReflectionMethod.php new file mode 100644 index 000000000..8d5380b32 --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,362 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionMethod; + +class StaticReflectionMethod extends ReflectionMethod +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the method. + * + * @var string + */ + protected $methodName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string $methodName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $methodName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->methodName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosure($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getPrototype() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invoke($object, $parameter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invokeArgs($object, array $args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDestructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosureThis() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfRequiredParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticVariables() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isClosure() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDeprecated() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function returnsReference() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/StaticReflectionParser.php b/wbce/include/Doctrine/Common/Reflection/StaticReflectionParser.php new file mode 100644 index 000000000..17bcd7c9f --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,309 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Annotations\TokenParser; +use ReflectionException; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Karoly Negyesi + */ +class StaticReflectionParser implements ReflectionProviderInterface +{ + /** + * The fully qualified class name. + * + * @var string + */ + protected $className; + + /** + * The short class name. + * + * @var string + */ + protected $shortClassName; + + /** + * Whether the caller only wants class annotations. + * + * @var boolean. + */ + protected $classAnnotationOptimize; + + /** + * Whether the parser has run. + * + * @var boolean + */ + protected $parsed = false; + + /** + * The namespace of the class. + * + * @var string + */ + protected $namespace = ''; + + /** + * The use statements of the class. + * + * @var array + */ + protected $useStatements = []; + + /** + * The docComment of the class. + * + * @var string + */ + protected $docComment = [ + 'class' => '', + 'property' => [], + 'method' => [] + ]; + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Reflection\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $className The full, namespaced class name. + * @param ClassFinderInterface $finder A ClassFinder object which finds the class. + * @param boolean $classAnnotationOptimize Only retrieve the class docComment. + * Presumes there is only one statement per line. + */ + public function __construct($className, $finder, $classAnnotationOptimize = false) + { + $this->className = ltrim($className, '\\'); + $lastNsPos = strrpos($this->className, '\\'); + + if ($lastNsPos !== false) { + $this->namespace = substr($this->className, 0, $lastNsPos); + $this->shortClassName = substr($this->className, $lastNsPos + 1); + } else { + $this->shortClassName = $this->className; + } + + $this->finder = $finder; + $this->classAnnotationOptimize = $classAnnotationOptimize; + } + + /** + * @return void + */ + protected function parse() + { + if ($this->parsed || !$fileName = $this->finder->findFile($this->className)) { + return; + } + $this->parsed = true; + $contents = file_get_contents($fileName); + if ($this->classAnnotationOptimize) { + if (preg_match("/\A.*^\s*((abstract|final)\s+)?class\s+{$this->shortClassName}\s+/sm", $contents, $matches)) { + $contents = $matches[0]; + } + } + $tokenParser = new TokenParser($contents); + $docComment = ''; + $last_token = false; + + while ($token = $tokenParser->next(false)) { + if (is_array($token)) {switch ($token[0]) { + case T_USE: + $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement()); + break; + case T_DOC_COMMENT: + $docComment = $token[1]; + break; + case T_CLASS: + if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM) {$this->docComment['class'] = $docComment; + $docComment = '';} + break; + case T_VAR: + case T_PRIVATE: + case T_PROTECTED: + case T_PUBLIC: + $token = $tokenParser->next(); + if ($token[0] === T_VARIABLE) { + $propertyName = substr($token[1], 1); + $this->docComment['property'][$propertyName] = $docComment; + continue 2; + } + if ($token[0] !== T_FUNCTION) { + // For example, it can be T_FINAL. + continue 2; + } + // No break. + case T_FUNCTION: + // The next string after function is the name, but + // there can be & before the function name so find the + // string. + while (($token = $tokenParser->next()) && $token[0] !== T_STRING); + $methodName = $token[1]; + $this->docComment['method'][$methodName] = $docComment; + $docComment = ''; + break; + case T_EXTENDS: + $this->parentClassName = $tokenParser->parseClass(); + $nsPos = strpos($this->parentClassName, '\\'); + $fullySpecified = false; + if ($nsPos === 0) { + $fullySpecified = true; + } else { + if ($nsPos) { + $prefix = strtolower(substr($this->parentClassName, 0, $nsPos)); + $postfix = substr($this->parentClassName, $nsPos); + } else { + $prefix = strtolower($this->parentClassName); + $postfix = ''; + } + foreach ($this->useStatements as $alias => $use) { + if ($alias == $prefix) { + $this->parentClassName = '\\' . $use . $postfix; + $fullySpecified = true; + } + } + } + if (!$fullySpecified) { + $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName; + } + break;} + } + + $last_token = $token[0]; + } + } + + /** + * @return StaticReflectionParser + */ + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + /** + * @return string + */ + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * {@inheritDoc} + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * {@inheritDoc} + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Gets the use statements from this file. + * + * @return array + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Gets the doc comment. + * + * @param string $type The type: 'class', 'property' or 'method'. + * @param string $name The name of the property or method, not needed for 'class'. + * + * @return string The doc comment, empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Gets the PSR-0 parser for the declaring class. + * + * @param string $type The type: 'property' or 'method'. + * @param string $name The name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + * + * @throws ReflectionException + */ + public function getStaticReflectionParserForDeclaringClass($type, $name) + { + $this->parse(); + if (isset($this->docComment[$type][$name])) { + return $this; + } + if (!empty($this->parentClassName)) { + return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name); + } + throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"'); + } +} diff --git a/wbce/include/Doctrine/Common/Reflection/StaticReflectionProperty.php b/wbce/include/Doctrine/Common/Reflection/StaticReflectionProperty.php new file mode 100644 index 000000000..7f6522bba --- /dev/null +++ b/wbce/include/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionProperty; + +class StaticReflectionProperty extends ReflectionProperty +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the property. + * + * @var string|null + */ + protected $propertyName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string|null $propertyName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $propertyName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->propertyName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDefault() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/wbce/include/Doctrine/Common/Util/ClassUtils.php b/wbce/include/Doctrine/Common/Util/ClassUtils.php new file mode 100644 index 000000000..49dc7bb17 --- /dev/null +++ b/wbce/include/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Persistence\Proxy; + +/** + * Class and reflection related functionality for objects that + * might or not be proxy objects at the moment. + * + * @author Benjamin Eberlei + * @author Johannes Schmitt + */ +class ClassUtils +{ + /** + * Gets the real class name of a class name that could be a proxy. + * + * @param string $class + * + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Gets the real class name of an object (even if its a proxy). + * + * @param object $object + * + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Gets the real parent class name of a class or object. + * + * @param string $className + * + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class( self::getRealClass( $className ) ); + } + + /** + * Creates a new reflection class. + * + * @param string $class + * + * @return \ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass( self::getRealClass( $class ) ); + } + + /** + * Creates a new reflection object. + * + * @param object $object + * + * @return \ReflectionObject + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass( self::getClass( $object ) ); + } + + /** + * Given a class name and a proxy namespace returns the proxy name. + * + * @param string $className + * @param string $proxyNamespace + * + * @return string + */ + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\'.Proxy::MARKER.'\\' . ltrim($className, '\\'); + } +} diff --git a/wbce/include/Doctrine/Common/Util/Debug.php b/wbce/include/Doctrine/Common/Util/Debug.php new file mode 100644 index 000000000..ae115edd7 --- /dev/null +++ b/wbce/include/Doctrine/Common/Util/Debug.php @@ -0,0 +1,197 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Proxy; + +/** + * Static class containing most used debug methods. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +final class Debug +{ + /** + * Private constructor (prevents instantiation). + */ + private function __construct() + { + } + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link http://xdebug.org/ + * + * @param mixed $var The variable to dump. + * @param integer $maxDepth The maximum nesting level for object properties. + * @param boolean $stripTags Whether output should strip HTML tags. + * @param boolean $echo Send the dumped value to the output buffer + * + * @return string + */ + public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) + { + $html = ini_get('html_errors'); + + if ($html !== true) { + ini_set('html_errors', true); + } + + if (extension_loaded('xdebug')) { + ini_set('xdebug.var_display_max_depth', $maxDepth); + } + + $var = self::export($var, $maxDepth); + + ob_start(); + var_dump($var); + + $dump = ob_get_contents(); + + ob_end_clean(); + + $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', $html); + + if ($echo) { + echo $dumpText; + } + + return $dumpText; + } + + /** + * @param mixed $var + * @param int $maxDepth + * + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($var instanceof Collection) { + $var = $var->toArray(); + } + + if (! $maxDepth) { + return is_object($var) ? get_class($var) + : (is_array($var) ? 'Array(' . count($var) . ')' : $var); + } + + if (is_array($var)) { + $return = []; + + foreach ($var as $k => $v) { + $return[$k] = self::export($v, $maxDepth - 1); + } + + return $return; + } + + if (! $isObj) { + return $var; + } + + $return = new \stdclass(); + if ($var instanceof \DateTimeInterface) { + $return->__CLASS__ = get_class($var); + $return->date = $var->format('c'); + $return->timezone = $var->getTimezone()->getName(); + + return $return; + } + + $return->__CLASS__ = ClassUtils::getClass($var); + + if ($var instanceof Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) { + $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); + } + + return self::fillReturnWithClassAttributes($var, $return, $maxDepth); + } + + /** + * Fill the $return variable with class attributes + * + * @param object $var + * @param stdClass $return + * @param int $maxDepth + * + * @return mixed + */ + private static function fillReturnWithClassAttributes($var, \stdClass $return, $maxDepth) + { + $reflClass = ClassUtils::newReflectionObject($var); + $parsedAttributes = array(); + do { + $currentClassName = $reflClass->getName(); + + foreach ($reflClass->getProperties() as $reflProperty) { + $attributeKey = $reflProperty->isPrivate() ? $currentClassName . '#' : ''; + $attributeKey .= $reflProperty->getName(); + + if (isset($parsedAttributes[$attributeKey])) { + continue; + } + + $parsedAttributes[$attributeKey] = true; + + $name = + $reflProperty->getName() + . ($return->__CLASS__ !== $currentClassName || $reflProperty->isPrivate() ? ':' . $currentClassName : '') + . ($reflProperty->isPrivate() ? ':private' : '') + . ($reflProperty->isProtected() ? ':protected' : '') + ; + + $reflProperty->setAccessible(true); + $return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1); + } + } while ($reflClass = $reflClass->getParentClass()); + + return $return; + } + + /** + * Returns a string representation of an object. + * + * @param object $obj + * + * @return string + */ + public static function toString($obj) + { + return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); + } +} diff --git a/wbce/include/Doctrine/Common/Util/Inflector.php b/wbce/include/Doctrine/Common/Util/Inflector.php new file mode 100644 index 000000000..082dc7894 --- /dev/null +++ b/wbce/include/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Inflector\Inflector as BaseInflector; + +/** + * Doctrine inflector has static methods for inflecting text. + * + * Kept for backwards compatibility reasons, was moved to its own component. + */ +class Inflector extends BaseInflector +{ +} diff --git a/wbce/include/Doctrine/Common/Version.php b/wbce/include/Doctrine/Common/Version.php new file mode 100644 index 000000000..09e500560 --- /dev/null +++ b/wbce/include/Doctrine/Common/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.7.3'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int -1 if older, 0 if it is the same, 1 if version passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/wbce/include/Doctrine/DBAL/Cache/ArrayStatement.php b/wbce/include/Doctrine/DBAL/Cache/ArrayStatement.php new file mode 100644 index 000000000..7417e9af3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $columnCount = 0; + + /** + * @var integer + */ + private $num = 0; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param array $data + */ + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + unset ($this->data); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->columnCount; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 !== null || $arg3 !== null) { + throw new \InvalidArgumentException("Caching layer does not support 2nd/3rd argument to setFetchMode()"); + } + + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + if (isset($this->data[$this->num])) { + $row = $this->data[$this->num++]; + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if ($fetchMode === PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode === PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode === PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for fetching result."); + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + + return $row[$columnIndex]; + } +} diff --git a/wbce/include/Doctrine/DBAL/Cache/CacheException.php b/wbce/include/Doctrine/DBAL/Cache/CacheException.php new file mode 100644 index 000000000..6b3b5394b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noResultDriverConfigured() + { + return new self("Trying to cache a query but no result driver is configured."); + } +} diff --git a/wbce/include/Doctrine/DBAL/Cache/QueryCacheProfile.php b/wbce/include/Doctrine/DBAL/Cache/QueryCacheProfile.php new file mode 100644 index 000000000..19f7a6096 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\Common\Cache\Cache; + +/** + * Query Cache Profile handles the data relevant for query caching. + * + * It is a value object, setter methods return NEW instances. + * + * @author Benjamin Eberlei + */ +class QueryCacheProfile +{ + /** + * @var \Doctrine\Common\Cache\Cache|null + */ + private $resultCacheDriver; + + /** + * @var integer + */ + private $lifetime = 0; + + /** + * @var string|null + */ + private $cacheKey; + + /** + * @param integer $lifetime + * @param string|null $cacheKey + * @param \Doctrine\Common\Cache\Cache|null $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + + return $this->cacheKey; + } + + /** + * Generates the real cache key from query, params, types and connection parameters. + * + * @param string $query + * @param array $params + * @param array $types + * @param array $connectionParams + * + * @return array + */ + public function generateCacheKeys($query, $params, $types, array $connectionParams = []) + { + $realCacheKey = 'query=' . $query . + '¶ms=' . serialize($params) . + '&types=' . serialize($types) . + '&connectionParams=' . serialize($connectionParams); + + // should the key be automatically generated using the inputs or is the cache key set? + if ($this->cacheKey === null) { + $cacheKey = sha1($realCacheKey); + } else { + $cacheKey = $this->cacheKey; + } + + return array($cacheKey, $realCacheKey); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param integer $lifetime + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setLifetime($lifetime) + { + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + } +} diff --git a/wbce/include/Doctrine/DBAL/Cache/ResultCacheStatement.php b/wbce/include/Doctrine/DBAL/Cache/ResultCacheStatement.php new file mode 100644 index 000000000..7a5921d05 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,221 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\Common\Cache\Cache; +use PDO; + +/** + * Cache statement for SQL results. + * + * A result is saved in multiple cache keys, there is the originally specified + * cache key which is just pointing to result rows by key. The following things + * have to be ensured: + * + * 1. lifetime of the original key has to be longer than that of all the individual rows keys + * 2. if any one row key is missing the query has to be re-executed. + * + * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. + * This means that the memory usage for cached results might increase by using this feature. + */ +class ResultCacheStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $resultCache; + + /** + * + * @var string + */ + private $cacheKey; + + /** + * @var string + */ + private $realKey; + + /** + * @var integer + */ + private $lifetime; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var boolean + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\Common\Cache\Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param integer $lifetime + */ + public function __construct(Statement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime) + { + $this->statement = $stmt; + $this->resultCache = $resultCache; + $this->cacheKey = $cacheKey; + $this->realKey = $realKey; + $this->lifetime = $lifetime; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->statement->closeCursor(); + if ($this->emptied && $this->data !== null) { + $data = $this->resultCache->fetch($this->cacheKey); + if ( ! $data) { + $data = array(); + } + $data[$this->realKey] = $this->data; + + $this->resultCache->save($this->cacheKey, $data, $this->lifetime); + unset($this->data); + } + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + if ($this->data === null) { + $this->data = array(); + } + + $row = $this->statement->fetch(PDO::FETCH_ASSOC); + if ($row) { + $this->data[] = $row; + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($fetchMode == PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode == PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode == PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + + return $row[$columnIndex]; + } + + /** + * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer The number of rows. + */ + public function rowCount() + { + return $this->statement->rowCount(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Configuration.php b/wbce/include/Doctrine/DBAL/Configuration.php new file mode 100644 index 000000000..ec7c13061 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Configuration.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\Common\Cache\Cache; + +/** + * Configuration container for the Doctrine DBAL. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal When adding a new configuration option just write a getter/setter + * pair and add the option to the _attributes array with a proper default value. + */ +class Configuration +{ + /** + * The attributes that are contained in the configuration. + * Values are default values. + * + * @var array + */ + protected $_attributes = array(); + + /** + * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. + * + * @param \Doctrine\DBAL\Logging\SQLLogger|null $logger + * + * @return void + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return \Doctrine\DBAL\Logging\SQLLogger|null + */ + public function getSQLLogger() + { + return isset($this->_attributes['sqlLogger']) ? + $this->_attributes['sqlLogger'] : null; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getResultCacheImpl() + { + return isset($this->_attributes['resultCacheImpl']) ? + $this->_attributes['resultCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * Sets the filter schema assets expression. + * + * Only include tables/sequences matching the filter expression regexp in + * schema instances generated for the active connection when calling + * {AbstractSchemaManager#createSchema()}. + * + * @param string $filterExpression + * + * @return void + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Returns filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + + return null; + } + + /** + * Sets the default auto-commit mode for connections. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see getAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $this->_attributes['autoCommit'] = (boolean) $autoCommit; + } + + /** + * Returns the default auto-commit mode for connections. + * + * @return boolean True if auto-commit mode is enabled by default for connections, false otherwise. + * + * @see setAutoCommit + */ + public function getAutoCommit() + { + if (isset($this->_attributes['autoCommit'])) { + return $this->_attributes['autoCommit']; + } + + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Connection.php b/wbce/include/Doctrine/DBAL/Connection.php new file mode 100644 index 000000000..082e413fd --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Connection.php @@ -0,0 +1,1677 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Exception\InvalidArgumentException; +use PDO; +use Closure; +use Exception; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Cache\ResultCacheStatement; +use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\DBAL\Cache\ArrayStatement; +use Doctrine\DBAL\Cache\CacheException; +use Doctrine\DBAL\Driver\PingableConnection; +use Throwable; + +/** + * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like + * events, transaction isolation levels, configuration, emulated transaction nesting, + * lazy connecting and more. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + * @author Lukas Smith (MDB2 library) + * @author Benjamin Eberlei + */ +class Connection implements DriverConnection +{ + /** + * Constant for transaction isolation level READ UNCOMMITTED. + */ + const TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * Constant for transaction isolation level READ COMMITTED. + */ + const TRANSACTION_READ_COMMITTED = 2; + + /** + * Constant for transaction isolation level REPEATABLE READ. + */ + const TRANSACTION_REPEATABLE_READ = 3; + + /** + * Constant for transaction isolation level SERIALIZABLE. + */ + const TRANSACTION_SERIALIZABLE = 4; + + /** + * Represents an array of ints to be expanded by Doctrine SQL parsing. + * + * @var integer + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var integer + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var integer + */ + const ARRAY_PARAM_OFFSET = 100; + + /** + * The wrapped driver connection. + * + * @var \Doctrine\DBAL\Driver\Connection + */ + protected $_conn; + + /** + * @var \Doctrine\DBAL\Configuration + */ + protected $_config; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + protected $_expr; + + /** + * Whether or not a connection has been established. + * + * @var boolean + */ + private $_isConnected = false; + + /** + * The current auto-commit mode of this connection. + * + * @var boolean + */ + private $autoCommit = true; + + /** + * The transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transactions should use savepoints. + * + * @var boolean + */ + private $_nestTransactionsWithSavepoints = false; + + /** + * The parameters used during creation of the Connection instance. + * + * @var array + */ + private $_params = array(); + + /** + * The DatabasePlatform object that provides information about the + * database platform used by the connection. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The schema manager. + * + * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + protected $_schemaManager; + + /** + * The used DBAL driver. + * + * @var \Doctrine\DBAL\Driver + */ + protected $_driver; + + /** + * Flag that indicates whether the current transaction is marked for rollback only. + * + * @var boolean + */ + private $_isRollbackOnly = false; + + /** + * @var integer + */ + protected $defaultFetchMode = PDO::FETCH_ASSOC; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param \Doctrine\DBAL\Driver $driver The driver to use. + * @param \Doctrine\DBAL\Configuration|null $config The configuration, optional. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager, optional. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, + EventManager $eventManager = null) + { + $this->_driver = $driver; + $this->_params = $params; + + if (isset($params['pdo'])) { + $this->_conn = $params['pdo']; + $this->_isConnected = true; + unset($this->_params['pdo']); + } + + if (isset($params["platform"])) { + if ( ! $params['platform'] instanceof Platforms\AbstractPlatform) { + throw DBALException::invalidPlatformType($params['platform']); + } + + $this->platform = $params["platform"]; + unset($this->_params["platform"]); + } + + // Create default config and event manager if none given + if ( ! $config) { + $config = new Configuration(); + } + + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $this->_config = $config; + $this->_eventManager = $eventManager; + + $this->_expr = new Query\Expression\ExpressionBuilder($this); + + $this->autoCommit = $config->getAutoCommit(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string|null + */ + public function getHost() + { + return isset($this->_params['host']) ? $this->_params['host'] : null; + } + + /** + * Gets the port of the currently connected database. + * + * @return mixed + */ + public function getPort() + { + return isset($this->_params['port']) ? $this->_params['port'] : null; + } + + /** + * Gets the username used by this connection. + * + * @return string|null + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string|null + */ + public function getPassword() + { + return isset($this->_params['password']) ? $this->_params['password'] : null; + } + + /** + * Gets the DBAL driver instance. + * + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_driver; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return \Doctrine\DBAL\Configuration + */ + public function getConfiguration() + { + return $this->_config; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the DatabasePlatform for the connection. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + if (null === $this->platform) { + $this->detectDatabasePlatform(); + } + + return $this->platform; + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function getExpressionBuilder() + { + return $this->_expr; + } + + /** + * Establishes the connection with the database. + * + * @return boolean TRUE if the connection was successfully established, FALSE if + * the connection is already open. + */ + public function connect() + { + if ($this->_isConnected) { + return false; + } + + $driverOptions = isset($this->_params['driverOptions']) ? + $this->_params['driverOptions'] : array(); + $user = isset($this->_params['user']) ? $this->_params['user'] : null; + $password = isset($this->_params['password']) ? + $this->_params['password'] : null; + + $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions); + $this->_isConnected = true; + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Detects and sets the database platform. + * + * Evaluates custom platform class and version in order to set the correct platform. + * + * @throws DBALException if an invalid platform was specified for this connection. + */ + private function detectDatabasePlatform() + { + $version = $this->getDatabasePlatformVersion(); + + if (null !== $version) { + $this->platform = $this->_driver->createDatabasePlatformForVersion($version); + } else { + $this->platform = $this->_driver->getDatabasePlatform(); + } + + $this->platform->setEventManager($this->_eventManager); + } + + /** + * Returns the version of the related platform if applicable. + * + * Returns null if either the driver is not capable to create version + * specific platform instances, no explicit server version was specified + * or the underlying driver connection cannot determine the platform + * version without having to query it (performance reasons). + * + * @return string|null + */ + private function getDatabasePlatformVersion() + { + // Driver does not support version specific platforms. + if ( ! $this->_driver instanceof VersionAwarePlatformDriver) { + return null; + } + + // Explicit platform version requested (supersedes auto-detection). + if (isset($this->_params['serverVersion'])) { + return $this->_params['serverVersion']; + } + + // If not connected, we need to connect now to determine the platform version. + if (null === $this->_conn) { + try { + $this->connect(); + } catch (\Exception $originalException) { + if (empty($this->_params['dbname'])) { + throw $originalException; + } + + // The database to connect to might not yet exist. + // Retry detection without database name connection parameter. + $databaseName = $this->_params['dbname']; + $this->_params['dbname'] = null; + + try { + $this->connect(); + } catch (\Exception $fallbackException) { + // Either the platform does not support database-less connections + // or something else went wrong. + // Reset connection parameters and rethrow the original exception. + $this->_params['dbname'] = $databaseName; + + throw $originalException; + } + + // Reset connection parameters. + $this->_params['dbname'] = $databaseName; + $serverVersion = $this->getServerVersion(); + + // Close "temporary" connection to allow connecting to the real database again. + $this->close(); + + return $serverVersion; + } + + } + + return $this->getServerVersion(); + } + + /** + * Returns the database server version if the underlying driver supports it. + * + * @return string|null + */ + private function getServerVersion() + { + // Automatic platform version detection. + if ($this->_conn instanceof ServerInfoAwareConnection && + ! $this->_conn->requiresQueryForServerVersion() + ) { + return $this->_conn->getServerVersion(); + } + + // Unable to detect platform version. + return null; + } + + /** + * Returns the current auto-commit mode for this connection. + * + * @return boolean True if auto-commit mode is currently enabled for this connection, false otherwise. + * + * @see setAutoCommit + */ + public function isAutoCommit() + { + return true === $this->autoCommit; + } + + /** + * Sets auto-commit mode for this connection. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is + * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see isAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $autoCommit = (boolean) $autoCommit; + + // Mode not changed, no-op. + if ($autoCommit === $this->autoCommit) { + return; + } + + $this->autoCommit = $autoCommit; + + // Commit all currently active transactions if any when switching auto-commit mode. + if (true === $this->_isConnected && 0 !== $this->_transactionNestingLevel) { + $this->commitAll(); + } + } + + /** + * Sets the fetch mode. + * + * @param integer $fetchMode + * + * @return void + */ + public function setFetchMode($fetchMode) + { + $this->defaultFetchMode = $fetchMode; + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $statement The SQL query. + * @param array $params The query parameters. + * @param array $types The query parameter types. + * + * @return array|bool False is returned if no rows are found. + */ + public function fetchAssoc($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetch(PDO::FETCH_ASSOC); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $statement The SQL query to be executed. + * @param array $params The prepared statement params. + * @param array $types The query parameter types. + * + * @return array|bool False is returned if no rows are found. + */ + public function fetchArray($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetch(PDO::FETCH_NUM); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $statement The SQL query to be executed. + * @param array $params The prepared statement params. + * @param integer $column The 0-indexed column number to retrieve. + * @param array $types The query parameter types. + * + * @return mixed|bool False is returned if no rows are found. + */ + public function fetchColumn($statement, array $params = array(), $column = 0, array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetchColumn($column); + } + + /** + * Whether an actual connection to the database is established. + * + * @return boolean + */ + public function isConnected() + { + return $this->_isConnected; + } + + /** + * Checks whether a transaction is currently active. + * + * @return boolean TRUE if a transaction is currently active, FALSE otherwise. + */ + public function isTransactionActive() + { + return $this->_transactionNestingLevel > 0; + } + + /** + * Gathers conditions for an update or delete call. + * + * @param array $identifiers Input array of columns to values + * + * @return string[][] a triplet with: + * - the first key being the columns + * - the second key being the values + * - the third key being the conditions + */ + private function gatherConditions(array $identifiers) + { + $columns = []; + $values = []; + $conditions = []; + + foreach ($identifiers as $columnName => $value) { + if (null === $value) { + $conditions[] = $this->getDatabasePlatform()->getIsNullExpression($columnName); + continue; + } + + $columns[] = $columnName; + $values[] = $value; + $conditions[] = $columnName . ' = ?'; + } + + return [$columns, $values, $conditions]; + } + + /** + * Executes an SQL DELETE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table on which to delete. + * @param array $identifier The deletion criteria. An associative array containing column-value pairs. + * @param array $types The types of identifiers. + * + * @return integer The number of affected rows. + * + * @throws InvalidArgumentException + */ + public function delete($tableExpression, array $identifier, array $types = array()) + { + if (empty($identifier)) { + throw InvalidArgumentException::fromEmptyCriteria(); + } + + list($columns, $values, $conditions) = $this->gatherConditions($identifier); + + return $this->executeUpdate( + 'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $conditions), + $values, + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + ); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + $this->_conn = null; + + $this->_isConnected = false; + } + + /** + * Sets the transaction isolation level. + * + * @param integer $level The level to set. + * + * @return integer + */ + public function setTransactionIsolation($level) + { + $this->_transactionIsolationLevel = $level; + + return $this->executeUpdate($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + if (null === $this->_transactionIsolationLevel) { + $this->_transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); + } + + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to update quoted or unquoted. + * @param array $data An associative array containing column-value pairs. + * @param array $identifier The update criteria. An associative array containing column-value pairs. + * @param array $types Types of the merged $data and $identifier arrays in that order. + * + * @return integer The number of affected rows. + */ + public function update($tableExpression, array $data, array $identifier, array $types = array()) + { + $setColumns = array(); + $setValues = array(); + $set = array(); + + foreach ($data as $columnName => $value) { + $setColumns[] = $columnName; + $setValues[] = $value; + $set[] = $columnName . ' = ?'; + } + + list($conditionColumns, $conditionValues, $conditions) = $this->gatherConditions($identifier); + $columns = array_merge($setColumns, $conditionColumns); + $values = array_merge($setValues, $conditionValues); + + if (is_string(key($types))) { + $types = $this->extractTypeValues($columns, $types); + } + + $sql = 'UPDATE ' . $tableExpression . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->executeUpdate($sql, $values, $types); + } + + /** + * Inserts a table row with specified data. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to insert data into, quoted or unquoted. + * @param array $data An associative array containing column-value pairs. + * @param array $types Types of the inserted data. + * + * @return integer The number of affected rows. + */ + public function insert($tableExpression, array $data, array $types = array()) + { + if (empty($data)) { + return $this->executeUpdate('INSERT INTO ' . $tableExpression . ' ()' . ' VALUES ()'); + } + + $columns = array(); + $values = array(); + $set = array(); + + foreach ($data as $columnName => $value) { + $columns[] = $columnName; + $values[] = $value; + $set[] = '?'; + } + + return $this->executeUpdate( + 'INSERT INTO ' . $tableExpression . ' (' . implode(', ', $columns) . ')' . + ' VALUES (' . implode(', ', $set) . ')', + $values, + is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types + ); + } + + /** + * Extract ordered type list from an ordered column list and type map. + * + * @param array $columnList + * @param array $types + * + * @return array + */ + private function extractTypeValues(array $columnList, array $types) + { + $typeValues = array(); + + foreach ($columnList as $columnIndex => $columnName) { + $typeValues[] = isset($types[$columnName]) + ? $types[$columnName] + : \PDO::PARAM_STR; + } + + return $typeValues; + } + + /** + * Quotes a string so it can be safely used as a table or column name, even if + * it is a reserved name. + * + * Delimiting style depends on the underlying database platform that is being used. + * + * NOTE: Just because you CAN use quoted identifiers does not mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The name to be quoted. + * + * @return string The quoted name. + */ + public function quoteIdentifier($str) + { + return $this->getDatabasePlatform()->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param int|null $type The type of the parameter. + * + * @return string The quoted parameter. + */ + public function quote($input, $type = null) + { + $this->connect(); + + list($value, $bindingType) = $this->getBindingInfo($input, $type); + + return $this->_conn->quote($value, $bindingType); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array. + * + * @param string $sql The SQL query. + * @param array $params The query parameters. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchAll($sql, array $params = array(), $types = array()) + { + return $this->executeQuery($sql, $params, $types)->fetchAll(); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * + * @return \Doctrine\DBAL\Statement The prepared statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function prepare($statement) + { + try { + $stmt = new Statement($statement, $this); + } catch (Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. + * + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + if ($qcp !== null) { + return $this->executeCacheQuery($query, $params, $types, $qcp); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + } else { + $stmt = $this->_conn->query($query); + } + } catch (Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $stmt; + } + + /** + * Executes a caching query. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp The query cache profile. + * + * @return \Doctrine\DBAL\Driver\ResultStatement + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) + { + $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl(); + if ( ! $resultCache) { + throw CacheException::noResultDriverConfigured(); + } + + list($cacheKey, $realKey) = $qcp->generateCacheKeys($query, $params, $types, $this->getParams()); + + // fetch the row pointers entry + if ($data = $resultCache->fetch($cacheKey)) { + // is the real key part of this row pointers map or is the cache only pointing to other cache keys? + if (isset($data[$realKey])) { + $stmt = new ArrayStatement($data[$realKey]); + } elseif (array_key_exists($realKey, $data)) { + $stmt = new ArrayStatement(array()); + } + } + + if (!isset($stmt)) { + $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime()); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query and returns the result, + * applying a given projection/transformation function on each row of the result. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters, if any. + * @param \Closure $function The transformation function that is applied on each row. + * The function receives a single parameter, an array, that + * represents a row of the result set. + * + * @return array The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\DBAL\DBALException + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + try { + $statement = $this->_conn->query(...$args); + } catch (Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]); + } + + $statement->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters + * and returns the number of affected rows. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $query The SQL query. + * @param array $params The query parameters. + * @param array $types The parameter types. + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + $result = $stmt->rowCount(); + } else { + $result = $this->_conn->exec($query); + } + } catch (Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function exec($statement) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($statement); + } + + try { + $result = $this->_conn->exec($statement); + } catch (Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Returns the current transaction nesting level. + * + * @return integer The nesting level. A value of 0 means there's no active transaction. + */ + public function getTransactionNestingLevel() + { + return $this->_transactionNestingLevel; + } + + /** + * Fetches the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + + return $this->_conn->errorCode(); + } + + /** + * Fetches extended error information associated with the last database operation. + * + * @return array The last error information. + */ + public function errorInfo() + { + $this->connect(); + + return $this->_conn->errorInfo(); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string|null $seqName Name of the sequence object from which the ID should be returned. + * + * @return string A string representation of the last inserted ID. + */ + public function lastInsertId($seqName = null) + { + $this->connect(); + + return $this->_conn->lastInsertId($seqName); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this Connection instance as an (optional) parameter. + * + * If an exception occurs during execution of the function or transaction commit, + * the transaction is rolled back and the exception re-thrown. + * + * @param \Closure $func The function to execute transactionally. + * + * @return mixed The value returned by $func + * + * @throws \Exception + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $res = $func($this); + $this->commit(); + return $res; + } catch (Exception $e) { + $this->rollBack(); + throw $e; + } catch (Throwable $e) { + $this->rollBack(); + throw $e; + } + } + + /** + * Sets if nested transactions should use savepoints. + * + * @param boolean $nestTransactionsWithSavepoints + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; + } + + /** + * Gets if nested transactions should use savepoints. + * + * @return boolean + */ + public function getNestTransactionsWithSavepoints() + { + return $this->_nestTransactionsWithSavepoints; + } + + /** + * Returns the savepoint name to use for nested transactions are false if they are not supported + * "savepointFormat" parameter is not set + * + * @return mixed A string with the savepoint name or false. + */ + protected function _getNestedTransactionSavePointName() + { + return 'DOCTRINE2_SAVEPOINT_'.$this->_transactionNestingLevel; + } + + /** + * Starts a transaction by suspending auto-commit mode. + * + * @return void + */ + public function beginTransaction() + { + $this->connect(); + + ++$this->_transactionNestingLevel; + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"START TRANSACTION"'); + } + $this->_conn->beginTransaction(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"SAVEPOINT"'); + } + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + } + + /** + * Commits the current transaction. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If the commit failed due to no active transaction or + * because the transaction was marked for rollback only. + */ + public function commit() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + if ($this->_isRollbackOnly) { + throw ConnectionException::commitFailedRollbackOnly(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"COMMIT"'); + } + $this->_conn->commit(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + + --$this->_transactionNestingLevel; + + if (false === $this->autoCommit && 0 === $this->_transactionNestingLevel) { + $this->beginTransaction(); + } + } + + /** + * Commits all current nesting transactions. + */ + private function commitAll() + { + while (0 !== $this->_transactionNestingLevel) { + if (false === $this->autoCommit && 1 === $this->_transactionNestingLevel) { + // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. + // Therefore we need to do the final commit here and then leave to avoid an infinite loop. + $this->commit(); + + return; + } + + $this->commit(); + } + } + + /** + * Cancels any database changes done during the current transaction. + * + * @throws \Doctrine\DBAL\ConnectionException If the rollback operation failed. + */ + public function rollBack() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"ROLLBACK"'); + } + $this->_transactionNestingLevel = 0; + $this->_conn->rollBack(); + $this->_isRollbackOnly = false; + if ($logger) { + $logger->stopQuery(); + } + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); + } + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->_transactionNestingLevel; + if ($logger) { + $logger->stopQuery(); + } + } else { + $this->_isRollbackOnly = true; + --$this->_transactionNestingLevel; + } + } + + /** + * Creates a new savepoint. + * + * @param string $savepoint The name of the savepoint to create. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function createSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->platform->createSavePoint($savepoint)); + } + + /** + * Releases the given savepoint. + * + * @param string $savepoint The name of the savepoint to release. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function releaseSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->platform->releaseSavePoint($savepoint)); + } + } + + /** + * Rolls back to the given savepoint. + * + * @param string $savepoint The name of the savepoint to rollback to. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function rollbackSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->platform->rollbackSavePoint($savepoint)); + } + + /** + * Gets the wrapped driver connection. + * + * @return \Doctrine\DBAL\Driver\Connection + */ + public function getWrappedConnection() + { + $this->connect(); + + return $this->_conn; + } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + if ( ! $this->_schemaManager) { + $this->_schemaManager = $this->_driver->getSchemaManager($this); + } + + return $this->_schemaManager; + } + + /** + * Marks the current transaction so that the only possible + * outcome for the transaction to be rolled back. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Checks whether the current transaction is marked for rollback only. + * + * @return boolean + * + * @throws \Doctrine\DBAL\ConnectionException If no transaction is active. + */ + public function isRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + return $this->_isRollbackOnly; + } + + /** + * Converts a given value to its database representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted value. + */ + public function convertToDatabaseValue($value, $type) + { + return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); + } + + /** + * Converts a given value to its PHP representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted type. + */ + public function convertToPHPValue($value, $type) + { + return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); + } + + /** + * Binds a set of parameters, some or all of which are typed with a PDO binding type + * or DBAL mapping type, to a given statement. + * + * @param \Doctrine\DBAL\Driver\Statement $stmt The statement to bind the values to. + * @param array $params The map/list of named/positional parameters. + * @param array $types The parameter types (PDO binding types or DBAL mapping types). + * + * @return void + * + * @internal Duck-typing used on the $stmt parameter to support driver statements as well as + * raw PDOStatement instances. + */ + private function _bindTypedValues($stmt, array $params, array $types) + { + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($bindIndex, $value, $bindingType); + } else { + $stmt->bindValue($bindIndex, $value); + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($name, $value, $bindingType); + } else { + $stmt->bindValue($name, $value); + } + } + } + } + + /** + * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type. + * + * @param mixed $value The value to bind. + * @param mixed $type The type to bind (PDO or DBAL). + * + * @return array [0] => the (escaped) value, [1] => the binding type. + */ + private function getBindingInfo($value, $type) + { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + + return array($value, $bindingType); + } + + /** + * Resolves the parameters to a format which can be displayed. + * + * @internal This is a purely internal method. If you rely on this method, you are advised to + * copy/paste the code as this method may change, or be removed without prior notice. + * + * @param array $params + * @param array $types + * + * @return array + */ + public function resolveParams(array $params, array $types) + { + $resolvedParams = array(); + + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$bindIndex] = $value; + } else { + $resolvedParams[$bindIndex] = $value; + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$name] = $value; + } else { + $resolvedParams[$name] = $value; + } + } + } + + return $resolvedParams; + } + + /** + * Creates a new instance of a SQL query builder. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } + + /** + * Ping the server + * + * When the server is not available the method returns FALSE. + * It is responsibility of the developer to handle this case + * and abort the request or reconnect manually: + * + * @example + * + * if ($conn->ping() === false) { + * $conn->close(); + * $conn->connect(); + * } + * + * It is undefined if the underlying driver attempts to reconnect + * or disconnect when the connection is not available anymore + * as long it returns TRUE when a reconnect succeeded and + * FALSE when the connection was dropped. + * + * @return bool + */ + public function ping() + { + $this->connect(); + + if ($this->_conn instanceof PingableConnection) { + return $this->_conn->ping(); + } + + try { + $this->query($this->getDatabasePlatform()->getDummySelectSQL()); + + return true; + } catch (DBALException $e) { + return false; + } + } +} diff --git a/wbce/include/Doctrine/DBAL/ConnectionException.php b/wbce/include/Doctrine/DBAL/ConnectionException.php new file mode 100644 index 000000000..86494b036 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; + +/** + * Master-Slave Connection + * + * Connection can be used with master-slave setups. + * + * Important for the understanding of this connection should be how and when + * it picks the slave or master. + * + * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection' + * or 'executeQuery' is used. + * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', + * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or + * 'prepare' is called. + * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. + * 4. One slave connection is randomly picked ONCE during a request. + * + * ATTENTION: You can write to the slave with this connection if you execute a write query without + * opening up a transaction. For example: + * + * $conn = DriverManager::getConnection(...); + * $conn->executeQuery("DELETE FROM table"); + * + * Be aware that Connection#executeQuery is a method specifically for READ + * operations only. + * + * This connection is limited to slave operations using the + * Connection#executeQuery operation only, because it wouldn't be compatible + * with the ORM or SchemaManager code otherwise. Both use all the other + * operations in a context where writes could happen to a slave, which makes + * this restricted approach necessary. + * + * You can manually connect to the master at any time by calling: + * + * $conn->connect('master'); + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', + * 'driver' => 'pdo_mysql', + * 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'slaves' => array( + * array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ) + * )); + * + * You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information. + * + * @author Lars Strojny + * @author Benjamin Eberlei + */ +class MasterSlaveConnection extends Connection +{ + /** + * Master and slave connection (one of the randomly picked slaves). + * + * @var \Doctrine\DBAL\Driver\Connection[] + */ + protected $connections = ['master' => null, 'slave' => null]; + + /** + * You can keep the slave connection and then switch back to it + * during the request if you know what you are doing. + * + * @var boolean + */ + protected $keepSlave = false; + + /** + * Creates Master Slave Connection. + * + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration|null $config + * @param \Doctrine\Common\EventManager|null $eventManager + * + * @throws \InvalidArgumentException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['slaves']) || !isset($params['master'])) { + throw new \InvalidArgumentException('master or slaves configuration missing'); + } + if (count($params['slaves']) == 0) { + throw new \InvalidArgumentException('You have to configure at least one slaves.'); + } + + $params['master']['driver'] = $params['driver']; + foreach ($params['slaves'] as $slaveKey => $slave) { + $params['slaves'][$slaveKey]['driver'] = $params['driver']; + } + + $this->keepSlave = isset($params['keepSlave']) ? (bool) $params['keepSlave'] : false; + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Checks if the connection is currently towards the master or not. + * + * @return boolean + */ + public function isConnectedToMaster() + { + return $this->_conn !== null && $this->_conn === $this->connections['master']; + } + + /** + * {@inheritDoc} + */ + public function connect($connectionName = null) + { + $requestedConnectionChange = ($connectionName !== null); + $connectionName = $connectionName ?: 'slave'; + + if ($connectionName !== 'slave' && $connectionName !== 'master') { + throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed."); + } + + // If we have a connection open, and this is not an explicit connection + // change request, then abort right here, because we are already done. + // This prevents writes to the slave in case of "keepSlave" option enabled. + if (isset($this->_conn) && $this->_conn && !$requestedConnectionChange) { + return false; + } + + $forceMasterAsSlave = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'master'; + $forceMasterAsSlave = true; + } + + if (isset($this->connections[$connectionName]) && $this->connections[$connectionName]) { + $this->_conn = $this->connections[$connectionName]; + + if ($forceMasterAsSlave && ! $this->keepSlave) { + $this->connections['slave'] = $this->_conn; + } + + return false; + } + + if ($connectionName === 'master') { + $this->connections['master'] = $this->_conn = $this->connectTo($connectionName); + + // Set slave connection to master to avoid invalid reads + if ( ! $this->keepSlave) { + $this->connections['slave'] = $this->connections['master']; + } + } else { + $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to a specific connection. + * + * @param string $connectionName + * + * @return \Doctrine\DBAL\Driver + */ + protected function connectTo($connectionName) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : []; + + $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string $connectionName + * @param array $params + * + * @return mixed + */ + protected function chooseConnectionConfiguration($connectionName, $params) + { + if ($connectionName === 'master') { + return $params['master']; + } + + return $params['slaves'][array_rand($params['slaves'])]; + } + + /** + * {@inheritDoc} + */ + public function executeUpdate($query, array $params = [], array $types = []) + { + $this->connect('master'); + + return parent::executeUpdate($query, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->connect('master'); + + parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + + parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->connect('master'); + + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier, array $types = []) + { + $this->connect('master'); + + return parent::delete($tableName, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function close() + { + unset($this->connections['master']); + unset($this->connections['slave']); + + parent::close(); + + $this->_conn = null; + $this->connections = array('master' => null, 'slave' => null); + } + + /** + * {@inheritDoc} + */ + public function update($tableName, array $data, array $identifier, array $types = []) + { + $this->connect('master'); + + return parent::update($tableName, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function insert($tableName, array $data, array $types = []) + { + $this->connect('master'); + + return parent::insert($tableName, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $this->connect('master'); + + return parent::exec($statement); + } + + /** + * {@inheritDoc} + */ + public function createSavepoint($savepoint) + { + $this->connect('master'); + + parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + parent::rollbackSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $this->connect('master'); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = $this->_conn->query(...$args); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * {@inheritDoc} + */ + public function prepare($statement) + { + $this->connect('master'); + + return parent::prepare($statement); + } +} diff --git a/wbce/include/Doctrine/DBAL/DBALException.php b/wbce/include/Doctrine/DBAL/DBALException.php new file mode 100644 index 000000000..a3643677f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/DBALException.php @@ -0,0 +1,290 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Driver\ExceptionConverterDriver; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class DBALException extends \Exception +{ + /** + * @param string $method + * + * @return \Doctrine\DBAL\DBALException + */ + public static function notSupported($method) + { + return new self("Operation '$method' is not supported by platform."); + } + + public static function invalidPlatformSpecified() : self + { + return new self( + "Invalid 'platform' option specified, need to give an instance of ". + "\Doctrine\DBAL\Platforms\AbstractPlatform."); + } + + /** + * @param mixed $invalidPlatform + */ + public static function invalidPlatformType($invalidPlatform) : self + { + if (\is_object($invalidPlatform)) { + return new self( + sprintf( + "Option 'platform' must be a subtype of '%s', instance of '%s' given", + AbstractPlatform::class, + \get_class($invalidPlatform) + ) + ); + } + + return new self( + sprintf( + "Option 'platform' must be an object and subtype of '%s'. Got '%s'", + AbstractPlatform::class, + \gettype($invalidPlatform) + ) + ); + } + + /** + * Returns a new instance for an invalid specified platform version. + * + * @param string $version The invalid platform version given. + * @param string $expectedFormat The expected platform version format. + * + * @return DBALException + */ + public static function invalidPlatformVersionSpecified($version, $expectedFormat) + { + return new self( + sprintf( + 'Invalid platform version "%s" specified. ' . + 'The platform version has to be specified in the format: "%s".', + $version, + $expectedFormat + ) + ); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidPdoInstance() + { + return new self( + "The 'pdo' option was used in DriverManager::getConnection() but no ". + "instance of PDO was given." + ); + } + + /** + * @param string|null $url The URL that was provided in the connection parameters (if any). + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverRequired($url = null) + { + if ($url) { + return new self( + sprintf( + "The options 'driver' or 'driverClass' are mandatory if a connection URL without scheme " . + "is given to DriverManager::getConnection(). Given URL: %s", + $url + ) + ); + } + + return new self("The options 'driver' or 'driverClass' are mandatory if no PDO ". + "instance is given to DriverManager::getConnection()."); + } + + /** + * @param string $unknownDriverName + * @param array $knownDrivers + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownDriver($unknownDriverName, array $knownDrivers) + { + return new self("The given 'driver' ".$unknownDriverName." is unknown, ". + "Doctrine currently supports only the following drivers: ".implode(", ", $knownDrivers)); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * @param string $sql + * @param array $params + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverExceptionDuringQuery(Driver $driver, \Exception $driverEx, $sql, array $params = array()) + { + $msg = "An exception occurred while executing '".$sql."'"; + if ($params) { + $msg .= " with params " . self::formatParameters($params); + } + $msg .= ":\n\n".$driverEx->getMessage(); + + return static::wrapException($driver, $driverEx, $msg); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverException(Driver $driver, \Exception $driverEx) + { + return static::wrapException($driver, $driverEx, "An exception occurred in driver: " . $driverEx->getMessage()); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * + * @return \Doctrine\DBAL\DBALException + */ + private static function wrapException(Driver $driver, \Exception $driverEx, $msg) + { + if ($driverEx instanceof Exception\DriverException) { + return $driverEx; + } + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof Driver\DriverException) { + return $driver->convertException($msg, $driverEx); + } + + return new self($msg, 0, $driverEx); + } + + /** + * Returns a human-readable representation of an array of parameters. + * This properly handles binary data by returning a hex representation. + * + * @param array $params + * + * @return string + */ + private static function formatParameters(array $params) + { + return '[' . implode(', ', array_map(function ($param) { + $json = @json_encode($param); + + if (! is_string($json) || $json == 'null' && is_string($param)) { + // JSON encoding failed, this is not a UTF-8 string. + return '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"'; + } + + return $json; + }, $params)) . ']'; + } + + /** + * @param string $wrapperClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidWrapperClass($wrapperClass) + { + return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ". + "subtype of \Doctrine\DBAL\Connection."); + } + + /** + * @param string $driverClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidDriverClass($driverClass) + { + return new self("The given 'driverClass' ".$driverClass." has to implement the ". + "\Doctrine\DBAL\Driver interface."); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidTableName($tableName) + { + return new self("Invalid table name specified: ".$tableName); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function noColumnsSpecifiedForTable($tableName) + { + return new self("No columns specified for table ".$tableName); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function limitOffsetInvalid() + { + return new self("Invalid Offset in Limit Query, it has to be larger than or equal to 0."); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownColumnType($name) + { + return new self('Unknown column type "'.$name.'" requested. Any Doctrine type that you use has ' . + 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . + 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . + 'introspection then you might have forgotten to register all database types for a Doctrine Type. Use ' . + 'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' . + 'Type#getMappedDatabaseTypes(). If the type name is empty you might ' . + 'have a problem with the cache or forgot some mapping information.' + ); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeNotFound($name) + { + return new self('Type to be overwritten '.$name.' does not exist.'); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver.php b/wbce/include/Doctrine/DBAL/Driver.php new file mode 100644 index 000000000..21d606466 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.0 + */ +interface Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string|null $username The username to use when connecting. + * @param string|null $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * + * @return \Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()); + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform(); + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param \Doctrine\DBAL\Connection $conn + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager(Connection $conn); + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName(); + + /** + * Gets the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * + * @return string The name of the database. + */ + public function getDatabase(Connection $conn); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractDB2Driver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractDB2Driver.php new file mode 100644 index 000000000..baa081690 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractDB2Driver.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\DB2Platform; +use Doctrine\DBAL\Schema\DB2SchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for IBM DB2 based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDB2Driver implements Driver +{ + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['dbname']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DB2Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DB2SchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractDriverException.php b/wbce/include/Doctrine/DBAL/Driver/AbstractDriverException.php new file mode 100644 index 000000000..84b95db10 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractDriverException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Abstract base implementation of the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDriverException extends \Exception implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param string $message The driver error message. + * @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any. + * @param integer|string|null $errorCode The driver specific error code if any. + */ + public function __construct($message, $sqlState = null, $errorCode = null) + { + parent::__construct($message); + + $this->errorCode = $errorCode; + $this->sqlState = $sqlState; + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractMySQLDriver.php new file mode 100644 index 000000000..a06d2c0ab --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -0,0 +1,186 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\MySQL57Platform; +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Schema\MySqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for MySQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1213': + return new Exception\DeadlockException($message, $exception); + case '1205': + return new Exception\LockWaitTimeoutException($message, $exception); + case '1050': + return new Exception\TableExistsException($message, $exception); + + case '1051': + case '1146': + return new Exception\TableNotFoundException($message, $exception); + + case '1216': + case '1217': + case '1451': + case '1452': + case '1701': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '1062': + case '1557': + case '1569': + case '1586': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '1054': + case '1166': + case '1611': + return new Exception\InvalidFieldNameException($message, $exception); + + case '1052': + case '1060': + case '1110': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '1064': + case '1149': + case '1287': + case '1341': + case '1342': + case '1343': + case '1344': + case '1382': + case '1479': + case '1541': + case '1554': + case '1626': + return new Exception\SyntaxErrorException($message, $exception); + + case '1044': + case '1045': + case '1046': + case '1049': + case '1095': + case '1142': + case '1143': + case '1227': + case '1370': + case '1429': + case '2002': + case '2005': + return new Exception\ConnectionException($message, $exception); + + case '1048': + case '1121': + case '1138': + case '1171': + case '1252': + case '1263': + case '1364': + case '1566': + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + if (false !== stripos($version, 'mariadb')) { + return $this->getDatabasePlatform(); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : null; + + if ('5' === $majorVersion && '7' === $minorVersion && null === $patchVersion) { + $patchVersion = '9'; + } + + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + if (version_compare($version, '5.7.9', '>=')) { + return new MySQL57Platform(); + } + + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new MySqlSchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractOracleDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractOracleDriver.php new file mode 100644 index 000000000..bffcc4a15 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractOracleDriver.php @@ -0,0 +1,155 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Schema\OracleSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Oracle based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1': + case '2299': + case '38911': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '904': + return new Exception\InvalidFieldNameException($message, $exception); + + case '918': + case '960': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '923': + return new Exception\SyntaxErrorException($message, $exception); + + case '942': + return new Exception\TableNotFoundException($message, $exception); + + case '955': + return new Exception\TableExistsException($message, $exception); + + case '1017': + case '12545': + return new Exception\ConnectionException($message, $exception); + + case '1400': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '2266': + case '2291': + case '2292': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['user']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new OraclePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new OracleSchemaManager($conn); + } + + /** + * Returns an appropriate Easy Connect String for the given parameters. + * + * @param array $params The connection parameters to return the Easy Connect STring for. + * + * @return string + * + * @link https://docs.oracle.com/database/121/NETAG/naming.htm + */ + protected function getEasyConnectString(array $params) + { + if ( ! empty($params['connectstring'])) { + return $params['connectstring']; + } + + if ( ! empty($params['host'])) { + if ( ! isset($params['port'])) { + $params['port'] = 1521; + } + + $serviceName = $params['dbname']; + + if ( ! empty($params['servicename'])) { + $serviceName = $params['servicename']; + } + + $service = 'SID=' . $serviceName; + $pooled = ''; + $instance = ''; + + if (isset($params['service']) && $params['service'] == true) { + $service = 'SERVICE_NAME=' . $serviceName; + } + + if (isset($params['instancename']) && ! empty($params['instancename'])) { + $instance = '(INSTANCE_NAME = ' . $params['instancename'] . ')'; + } + + if (isset($params['pooled']) && $params['pooled'] == true) { + $pooled = '(SERVER=POOLED)'; + } + + return '(DESCRIPTION=' . + '(ADDRESS=(PROTOCOL=TCP)(HOST=' . $params['host'] . ')(PORT=' . $params['port'] . '))' . + '(CONNECT_DATA=(' . $service . ')' . $instance . $pooled . '))'; + + } + + return isset($params['dbname']) ? $params['dbname'] : ''; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php new file mode 100644 index 000000000..31f48ee77 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php @@ -0,0 +1,154 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\PostgreSQL91Platform; +use Doctrine\DBAL\Platforms\PostgreSQL92Platform; +use Doctrine\DBAL\Platforms\PostgreSQL94Platform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for PostgreSQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getSQLState()) { + case '40001': + case '40P01': + return new Exception\DeadlockException($message, $exception); + case '0A000': + // Foreign key constraint violations during a TRUNCATE operation + // are considered "feature not supported" in PostgreSQL. + if (strpos($exception->getMessage(), 'truncate') !== false) { + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + break; + case '23502': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '23503': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '23505': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '42601': + return new Exception\SyntaxErrorException($message, $exception); + + case '42702': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '42703': + return new Exception\InvalidFieldNameException($message, $exception); + + case '42P01': + return new Exception\TableNotFoundException($message, $exception); + + case '42P07': + return new Exception\TableExistsException($message, $exception); + + case '7': + // In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code. + // The exception code is always set to 7 here. + // We have to match against the SQLSTATE in the error message in these cases. + if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + break; + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + switch(true) { + case version_compare($version, '9.4', '>='): + return new PostgreSQL94Platform(); + case version_compare($version, '9.2', '>='): + return new PostgreSQL92Platform(); + case version_compare($version, '9.1', '>='): + return new PostgreSQL91Platform(); + default: + return new PostgreSqlPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return (isset($params['dbname'])) + ? $params['dbname'] + : $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new PostgreSqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new PostgreSqlSchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php new file mode 100644 index 000000000..d6c5944be --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php @@ -0,0 +1,149 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SQLAnywhere11Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere12Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; +use Doctrine\DBAL\Platforms\SQLAnywherePlatform; +use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '-306': + case '-307': + case '-684': + return new Exception\DeadlockException($message, $exception); + case '-210': + case '-1175': + case '-1281': + return new Exception\LockWaitTimeoutException($message, $exception); + case '-100': + case '-103': + case '-832': + return new Exception\ConnectionException($message, $exception); + case '-143': + return new Exception\InvalidFieldNameException($message, $exception); + case '-193': + case '-196': + return new Exception\UniqueConstraintViolationException($message, $exception); + case '-194': + case '-198': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + case '-144': + return new Exception\NonUniqueFieldNameException($message, $exception); + case '-184': + case '-195': + return new Exception\NotNullConstraintViolationException($message, $exception); + case '-131': + return new Exception\SyntaxErrorException($message, $exception); + case '-110': + return new Exception\TableExistsException($message, $exception); + case '-141': + case '-1041': + return new Exception\TableNotFoundException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '16', '>='): + return new SQLAnywhere16Platform(); + case version_compare($version, '12', '>='): + return new SQLAnywhere12Platform(); + case version_compare($version, '11', '>='): + return new SQLAnywhere11Platform(); + default: + return new SQLAnywherePlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLAnywhere12Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLAnywhereSchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php new file mode 100644 index 000000000..53a4dd526 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\SQLServer2005Platform; +use Doctrine\DBAL\Platforms\SQLServer2008Platform; +use Doctrine\DBAL\Platforms\SQLServer2012Platform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Schema\SQLServerSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Microsoft SQL Server based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '11.00.2100', '>='): + return new SQLServer2012Platform(); + case version_compare($version, '10.00.1600', '>='): + return new SQLServer2008Platform(); + case version_compare($version, '9.00.1399', '>='): + return new SQLServer2005Platform(); + default: + return new SQLServerPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLServer2008Platform(); + } + + /** + * {@inheritdoc} + */ + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLServerSchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php new file mode 100644 index 000000000..92368ed6e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php @@ -0,0 +1,117 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Schema\SqliteSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.sqlite.org/c3ref/c_abort.html + */ + public function convertException($message, DriverException $exception) + { + if (strpos($exception->getMessage(), 'database is locked') !== false) { + return new Exception\LockWaitTimeoutException($message, $exception); + } + + if (strpos($exception->getMessage(), 'must be unique') !== false || + strpos($exception->getMessage(), 'is not unique') !== false || + strpos($exception->getMessage(), 'are not unique') !== false || + strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false + ) { + return new Exception\UniqueConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'may not be NULL') !== false || + strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false + ) { + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'no such table:') !== false) { + return new Exception\TableNotFoundException($message, $exception); + } + + if (strpos($exception->getMessage(), 'already exists') !== false) { + return new Exception\TableExistsException($message, $exception); + } + + if (strpos($exception->getMessage(), 'has no column named') !== false) { + return new Exception\InvalidFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { + return new Exception\NonUniqueFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'syntax error') !== false) { + return new Exception\SyntaxErrorException($message, $exception); + } + + if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { + return new Exception\ReadOnlyException($message, $exception); + } + + if (strpos($exception->getMessage(), 'unable to open database file') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return isset($params['path']) ? $params['path'] : null; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SqlitePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SqliteSchemaManager($conn); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Connection.php b/wbce/include/Doctrine/DBAL/Driver/Connection.php new file mode 100644 index 000000000..24cd5485a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Connection interface. + * Driver connections must implement this interface. + * + * This resembles (a subset of) the PDO interface. + * + * @since 2.0 + */ +interface Connection +{ + /** + * Prepares a statement for execution and returns a Statement object. + * + * @param string $prepareString + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function prepare($prepareString); + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function query(); + + /** + * Quotes a string for use in a query. + * + * @param mixed $input + * @param integer $type + * + * @return mixed + */ + function quote($input, $type=\PDO::PARAM_STR); + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer + */ + function exec($statement); + + /** + * Returns the ID of the last inserted row or sequence value. + * + * @param string|null $name + * + * @return string + */ + function lastInsertId($name = null); + + /** + * Initiates a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function beginTransaction(); + + /** + * Commits a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function commit(); + + /** + * Rolls back the current transaction, as initiated by beginTransaction(). + * + * @return boolean TRUE on success or FALSE on failure. + */ + function rollBack(); + + /** + * Returns the error code associated with the last operation on the database handle. + * + * @return string|null The error code, or null if no operation has been run on the database handle. + */ + function errorCode(); + + /** + * Returns extended error information associated with the last operation on the database handle. + * + * @return array + */ + function errorInfo(); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/DriverException.php b/wbce/include/Doctrine/DBAL/Driver/DriverException.php new file mode 100644 index 000000000..a96ccdce9 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/DriverException.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver exception. + * + * Driver exceptions provide the SQLSTATE of the driver + * and the driver specific error code at the time the error occurred. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface DriverException extends \Throwable +{ + /** + * Returns the driver specific error code if available. + * + * Returns null if no driver specific error code is available + * for the error raised by the driver. + * + * @return integer|string|null + */ + public function getErrorCode(); + + /** + * Returns the driver error message. + * + * @return string + */ + public function getMessage(); + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred. + * + * Returns null if the driver does not provide a SQLSTATE for the error occurred. + * + * @return string|null + */ + public function getSQLState(); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php new file mode 100644 index 000000000..92ca6601f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * @author Kim Hemsø Rasmussen + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection +{ + /** + * {@inheritdoc} + */ + public function quote($value, $type = \PDO::PARAM_STR) + { + if (\PDO::PARAM_BOOL === $type) { + if ($value) { + return 'true'; + } else { + return 'false'; + } + } + + return parent::quote($value, $type); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php new file mode 100644 index 000000000..547fdb795 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +use Doctrine\DBAL\Platforms\DrizzlePlatform; +use Doctrine\DBAL\Schema\DrizzleSchemaManager; + +/** + * Drizzle driver using PDO MySql. + * + * @author Kim Hemsø Rasmussen + */ +class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + $conn = new Connection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DrizzlePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DrizzleSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle_pdo_mysql'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/ExceptionConverterDriver.php b/wbce/include/Doctrine/DBAL/Driver/ExceptionConverterDriver.php new file mode 100644 index 000000000..0bd8d516e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/ExceptionConverterDriver.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ExceptionConverterDriver +{ + /** + * Converts a given DBAL driver exception into a standardized DBAL driver exception. + * + * It evaluates the vendor specific error code and SQLSTATE and transforms + * it into a unified {@link Doctrine\DBAL\Exception\DriverException} subclass. + * + * @param string $message The DBAL exception message to use. + * @param \Doctrine\DBAL\Driver\DriverException $exception The DBAL driver exception to convert. + * + * @return \Doctrine\DBAL\Exception\DriverException An instance of one of the DriverException subclasses. + */ + public function convertException($message, DriverException $exception); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php new file mode 100644 index 000000000..263c6d446 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +class DB2Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + private $_conn = null; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\IBMDB2\DB2Exception + */ + public function __construct(array $params, $username, $password, $driverOptions = []) + { + $isPersistent = (isset($params['persistent']) && $params['persistent'] == true); + + if ($isPersistent) { + $this->_conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); + } else { + $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); + } + if ( ! $this->_conn) { + throw new DB2Exception(db2_conn_errormsg()); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = db2_server_info($this->_conn); + + return $serverInfo->DBMS_VER; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if ( ! $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return new DB2Statement($stmt); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT) { + return $input; + } else { + return "'".$input."'"; + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = @db2_exec($this->_conn, $statement); + + if (false === $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return db2_num_rows($stmt); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return [ + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ]; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php new file mode 100644 index 000000000..de5921edd --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; + +/** + * IBM DB2 Driver. + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + if ( ! isset($params['protocol'])) { + $params['protocol'] = 'TCPIP'; + } + + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { + // if the host isn't localhost, use extended connection params + $params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $params['dbname'] . + ';HOSTNAME=' . $params['host'] . + ';PROTOCOL=' . $params['protocol'] . + ';UID=' . $username . + ';PWD=' . $password .';'; + if (isset($params['port'])) { + $params['dbname'] .= 'PORT=' . $params['port']; + } + + $username = null; + $password = null; + } + + return new DB2Connection($params, $username, $password, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ibm_db2'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php new file mode 100644 index 000000000..72775cbfe --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Exception extends \Exception +{ +} diff --git a/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php new file mode 100644 index 000000000..c2355cbc6 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,362 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + /** + * @var resource + */ + private $_stmt; + + /** + * @var array + */ + private $_bindParam = []; + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = []; + + /** + * @var integer + */ + private $_defaultFetchMode = \PDO::FETCH_BOTH; + + /** + * Indicates whether the statement is in the state when fetching results is possible + * + * @var bool + */ + private $result = false; + + /** + * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG + * + * @var array + */ + static private $_typeMap = [ + \PDO::PARAM_INT => DB2_LONG, + \PDO::PARAM_STR => DB2_CHAR, + ]; + + /** + * @param resource $stmt + */ + public function __construct($stmt) + { + $this->_stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $this->_bindParam[$column] =& $variable; + + if ($type && isset(self::$_typeMap[$type])) { + $type = self::$_typeMap[$type]; + } else { + $type = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if ( ! $this->_stmt) { + return false; + } + + $this->_bindParam = []; + + if (!db2_free_result($this->_stmt)) { + return false; + } + + $this->result = false; + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + if ( ! $this->_stmt) { + return false; + } + + return db2_num_fields($this->_stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_stmt_error(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return [ + db2_stmt_errormsg(), + db2_stmt_error(), + ]; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ( ! $this->_stmt) { + return false; + } + + if ($params === null) { + ksort($this->_bindParam); + + $params = []; + + foreach ($this->_bindParam as $column => $value) { + $params[] = $value; + } + } + + $retval = @db2_execute($this->_stmt, $params); + + if ($retval === false) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + $this->result = true; + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return false; + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + switch ($fetchMode) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : []; + } + + $result = db2_fetch_object($this->_stmt); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + case \PDO::FETCH_OBJ: + return db2_fetch_object($this->_stmt); + default: + throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.'); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $rows = []; + + switch ($fetchMode) { + case \PDO::FETCH_CLASS: + while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) { + $rows[] = $row; + } + break; + case \PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt)) ? : 0; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws DB2Exception + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = []) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new DB2Exception(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + /** @var \ReflectionProperty[] $destinationProperties */ + $destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + // Try to find a case-matching property. + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $name = strtolower($name); + + // Try to find a property without matching case. + // Fallback for the driver returning either all uppercase or all lowercase column names. + if (isset($destinationProperties[$name])) { + $destinationProperty = $destinationProperties[$name]; + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $destinationClass->$name = $value; + } + + return $destinationClass; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Mysqli/Driver.php b/wbce/include/Doctrine/DBAL/Driver/Mysqli/Driver.php new file mode 100644 index 000000000..d04e194d1 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\DBALException; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + return new MysqliConnection($params, $username, $password, $driverOptions); + } catch (MysqliException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php new file mode 100644 index 000000000..9d30b9686 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,292 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; +use Doctrine\DBAL\Driver\PingableConnection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * @author Kim Hemsø Rasmussen + * @author Till Klampaeckel + */ +class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection +{ + /** + * Name of the option to set connection flags + */ + const OPTION_FLAGS = 'flags'; + + /** + * @var \mysqli + */ + private $_conn; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + public function __construct(array $params, $username, $password, array $driverOptions = []) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + + // Fallback to default MySQL port if not given. + if ( ! $port) { + $port = 3306; + } + + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + $dbname = isset($params['dbname']) ? $params['dbname'] : null; + + $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null; + + $this->_conn = mysqli_init(); + + $this->setSecureConnection($params); + $this->setDriverOptions($driverOptions); + + set_error_handler(function () {}); + try { + if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) { + throw new MysqliException($this->_conn->connect_error, $this->_conn->sqlstate ?? 'HY000', $this->_conn->connect_errno); + } + } finally { + restore_error_handler(); + } + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieves mysqli native resource handle. + * + * Could be used if part of your application is not using DBAL. + * + * @return \mysqli + */ + public function getWrappedResourceHandle() + { + return $this->_conn; + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $majorVersion = floor($this->_conn->server_version / 10000); + $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100); + $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100); + + return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new MysqliStatement($this->_conn, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + return "'". $this->_conn->escape_string($input) ."'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + if (false === $this->_conn->query($statement)) { + throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + } + + return $this->_conn->affected_rows; + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return $this->_conn->insert_id; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->_conn->query('START TRANSACTION'); + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->_conn->commit(); + } + + /** + * {@inheritdoc}non-PHPdoc) + */ + public function rollBack() + { + return $this->_conn->rollback(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_conn->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_conn->error; + } + + /** + * Apply the driver options to the connection. + * + * @param array $driverOptions + * + * @throws MysqliException When one of of the options is not supported. + * @throws MysqliException When applying doesn't work - e.g. due to incorrect value. + */ + private function setDriverOptions(array $driverOptions = []) + { + $supportedDriverOptions = [ + \MYSQLI_OPT_CONNECT_TIMEOUT, + \MYSQLI_OPT_LOCAL_INFILE, + \MYSQLI_INIT_COMMAND, + \MYSQLI_READ_DEFAULT_FILE, + \MYSQLI_READ_DEFAULT_GROUP, + ]; + + if (defined('MYSQLI_SERVER_PUBLIC_KEY')) { + $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY; + } + + $exceptionMsg = "%s option '%s' with value '%s'"; + + foreach ($driverOptions as $option => $value) { + + if ($option === static::OPTION_FLAGS) { + continue; + } + + if (!in_array($option, $supportedDriverOptions, true)) { + throw new MysqliException( + sprintf($exceptionMsg, 'Unsupported', $option, $value) + ); + } + + if (@mysqli_options($this->_conn, $option, $value)) { + continue; + } + + $msg = sprintf($exceptionMsg, 'Failed to set', $option, $value); + $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn)); + + throw new MysqliException( + $msg, + $this->_conn->sqlstate, + $this->_conn->errno + ); + } + } + + /** + * Pings the server and re-connects when `mysqli.reconnect = 1` + * + * @return bool + */ + public function ping() + { + return $this->_conn->ping(); + } + + /** + * Establish a secure connection + * + * @param array $params + * @throws MysqliException + */ + private function setSecureConnection(array $params) + { + if (! isset($params['ssl_key']) && + ! isset($params['ssl_cert']) && + ! isset($params['ssl_ca']) && + ! isset($params['ssl_capath']) && + ! isset($params['ssl_cipher']) + ) { + return; + } + + $this->_conn->ssl_set( + $params['ssl_key'] ?? null, + $params['ssl_cert'] ?? null, + $params['ssl_ca'] ?? null, + $params['ssl_capath'] ?? null, + $params['ssl_cipher'] ?? null + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliException.php new file mode 100644 index 000000000..23728b113 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * Exception thrown in case the mysqli driver errors. + * + * @author Kim Hemsø Rasmussen + * @author Steve Müller + */ +class MysqliException extends AbstractDriverException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php new file mode 100644 index 000000000..57d7e3ee7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,413 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + /** + * @var array + */ + protected static $_paramTypeMap = [ + PDO::PARAM_STR => 's', + PDO::PARAM_BOOL => 'i', + PDO::PARAM_NULL => 's', + PDO::PARAM_INT => 'i', + PDO::PARAM_LOB => 's' // TODO Support LOB bigger then max package size. + ]; + + /** + * @var \mysqli + */ + protected $_conn; + + /** + * @var \mysqli_stmt + */ + protected $_stmt; + + /** + * @var null|boolean|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * @var string + */ + protected $types; + + /** + * Contains ref values for bindValue(). + * + * @var array + */ + protected $_values = []; + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Indicates whether the statement is in the state when fetching results is possible + * + * @var bool + */ + private $result = false; + + /** + * @param \mysqli $conn + * @param string $prepareString + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + public function __construct(\mysqli $conn, $prepareString) + { + $this->_conn = $conn; + $this->_stmt = $conn->prepare($prepareString); + if (false === $this->_stmt) { + throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + $this->types = str_repeat('s', $paramCount); + $this->_bindedValues = array_fill(1, $paramCount, null); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->types[$column - 1] = $type; + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_values[$param] = $value; + $this->_bindedValues[$param] =& $this->_values[$param]; + $this->types[$param - 1] = $type; + + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if (null !== $this->_bindedValues) { + if (null !== $params) { + if ( ! $this->_bindValues($params)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + if (!call_user_func_array([$this->_stmt, 'bind_param'], [$this->types] + $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + } + } + + if ( ! $this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + $columnNames = []; + foreach ($meta->fetch_fields() as $col) { + $columnNames[] = $col->name; + } + $meta->free(); + + $this->_columnNames = $columnNames; + } else { + $this->_columnNames = false; + } + } + + if (false !== $this->_columnNames) { + // Store result of every execution which has it. Otherwise it will be impossible + // to execute a new statement in case if the previous one has non-fetched rows + // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html + $this->_stmt->store_result(); + + // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql, + // it will have to allocate as much memory as it may be needed for the given column type + // (e.g. for a LONGBLOB field it's 4 gigabytes) + // @link https://bugs.php.net/bug.php?id=51386#1270673122 + // + // Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been + // previously called on the statement, the values are unbound making the statement unusable. + // + // It's also important that row values are bound after _each_ call to store_result(). Otherwise, + // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated + // to the length of the ones fetched during the previous execution. + $this->_rowBindedValues = array_fill(0, count($this->_columnNames), null); + + $refs = []; + foreach ($this->_rowBindedValues as $key => &$value) { + $refs[$key] =& $value; + } + + if (!call_user_func_array([$this->_stmt, 'bind_result'], $refs)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + } + + $this->result = true; + + return true; + } + + /** + * Binds a array of values to bound parameters. + * + * @param array $values + * + * @return boolean + */ + private function _bindValues($values) + { + $params = []; + $types = str_repeat('s', count($values)); + $params[0] = $types; + + foreach ($values as &$v) { + $params[] =& $v; + } + + return call_user_func_array([$this->_stmt, 'bind_param'], $params); + } + + /** + * @return boolean|array + */ + private function _fetch() + { + $ret = $this->_stmt->fetch(); + + if (true === $ret) { + $values = []; + foreach ($this->_rowBindedValues as $v) { + $values[] = $v; + } + + return $values; + } + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return false; + } + + $values = $this->_fetch(); + if (null === $values) { + return false; + } + + if (false === $values) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_NUM: + return $values; + + case PDO::FETCH_ASSOC: + return array_combine($this->_columnNames, $values); + + case PDO::FETCH_BOTH: + $ret = array_combine($this->_columnNames, $values); + $ret += $values; + + return $ret; + + case PDO::FETCH_OBJ: + $assoc = array_combine($this->_columnNames, $values); + $ret = new \stdClass(); + + foreach ($assoc as $column => $value) { + $ret->$column = $value; + } + + return $ret; + + default: + throw new MysqliException("Unknown fetch type '{$fetchMode}'"); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + $rows = []; + if (PDO::FETCH_COLUMN == $fetchMode) { + while (($row = $this->fetchColumn()) !== false) { + $rows[] = $row; + } + } else { + while (($row = $this->fetch($fetchMode)) !== false) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_stmt->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_stmt->error; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->_stmt->free_result(); + $this->result = false; + + return true; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + if (false === $this->_columnNames) { + return $this->_stmt->affected_rows; + } + + return $this->_stmt->num_rows; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->_stmt->field_count; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/OCI8/Driver.php b/wbce/include/Doctrine/DBAL/Driver/OCI8/Driver.php new file mode 100644 index 000000000..c4552bb18 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + return new OCI8Connection( + $username, + $password, + $this->_constructDsn($params), + isset($params['charset']) ? $params['charset'] : null, + isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT, + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (OCI8Exception $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + return $this->getEasyConnectString($params); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'oci8'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php new file mode 100644 index 000000000..ecf389911 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Platforms\OraclePlatform; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $dbh; + + /** + * @var integer + */ + protected $executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Creates a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + * @param string|null $charset + * @param integer $sessionMode + * @param boolean $persistent + * + * @throws OCI8Exception + */ + public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT, $persistent = false) + { + if (!defined('OCI_NO_AUTO_COMMIT')) { + define('OCI_NO_AUTO_COMMIT', 0); + } + + $this->dbh = $persistent + ? @oci_pconnect($username, $password, $db, $charset, $sessionMode) + : @oci_connect($username, $password, $db, $charset, $sessionMode); + + if ( ! $this->dbh) { + throw OCI8Exception::fromErrorInfo(oci_error()); + } + } + + /** + * {@inheritdoc} + * + * @throws \UnexpectedValueException if the version string returned by the database server + * does not contain a parsable version number. + */ + public function getServerVersion() + { + if ( ! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) { + throw new \UnexpectedValueException( + sprintf( + 'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' . + 'Please report this database version string to the Doctrine team.', + oci_server_version($this->dbh) + ) + ); + } + + return $version[1]; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->dbh, $prepareString, $this); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return false; + } + + $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; + $stmt = $this->query($sql); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + if ($result === false || !isset($result['CURRVAL'])) { + throw new OCI8Exception("lastInsertId failed: Query was executed but no result was returned."); + } + + return (int) $result['CURRVAL']; + } + + /** + * Returns the current execution mode. + * + * @return integer + */ + public function getExecuteMode() + { + return $this->executeMode; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->executeMode = OCI_NO_AUTO_COMMIT; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!oci_commit($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!oci_rollback($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->dbh); + if ($error !== false) { + $error = $error['code']; + } + + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->dbh); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php new file mode 100644 index 000000000..1cd08b8a4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +class OCI8Exception extends AbstractDriverException +{ + /** + * @param array $error + * + * @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception + */ + public static function fromErrorInfo($error) + { + return new self($error['message'], null, $error['code']); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php new file mode 100644 index 000000000..8831a2258 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,480 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * The OCI8 implementation of the Statement interface. + * + * @since 2.0 + * @author Roman Borschel + */ +class OCI8Statement implements \IteratorAggregate, Statement +{ + /** + * @var resource + */ + protected $_dbh; + + /** + * @var resource + */ + protected $_sth; + + /** + * @var \Doctrine\DBAL\Driver\OCI8\OCI8Connection + */ + protected $_conn; + + /** + * @var string + */ + protected static $_PARAM = ':param'; + + /** + * @var array + */ + protected static $fetchModeMap = [ + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::FETCH_COLUMN => OCI_NUM, + ]; + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var array + */ + protected $_paramMap = []; + + /** + * Holds references to bound parameter values. + * + * This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected. + * + * @var array + */ + private $boundValues = []; + + /** + * Indicates whether the statement is in the state when fetching results is possible + * + * @var bool + */ + private $result = false; + + /** + * Creates a new OCI8Statement that uses the given connection handle and SQL statement. + * + * @param resource $dbh The connection handle. + * @param string $statement The SQL statement. + * @param \Doctrine\DBAL\Driver\OCI8\OCI8Connection $conn + */ + public function __construct($dbh, $statement, OCI8Connection $conn) + { + list($statement, $paramMap) = self::convertPositionalToNamedPlaceholders($statement); + $this->_sth = oci_parse($dbh, $statement); + $this->_dbh = $dbh; + $this->_paramMap = $paramMap; + $this->_conn = $conn; + } + + /** + * Converts positional (?) into named placeholders (:param). + * + * Oracle does not support positional parameters, hence this method converts all + * positional parameters into artificially named parameters. Note that this conversion + * is not perfect. All question marks (?) in the original statement are treated as + * placeholders and converted to a named parameter. + * + * The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral. + * Question marks inside literal strings are therefore handled correctly by this method. + * This comes at a cost, the whole sql statement has to be looped over. + * + * @todo extract into utility class in Doctrine\DBAL\Util namespace + * @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements. + * + * @param string $statement The SQL statement to convert. + * + * @return string + * @throws \Doctrine\DBAL\Driver\OCI8\OCI8Exception + */ + static public function convertPositionalToNamedPlaceholders($statement) + { + $fragmentOffset = $tokenOffset = 0; + $fragments = $paramMap = []; + $currentLiteralDelimiter = null; + + do { + if (!$currentLiteralDelimiter) { + $result = self::findPlaceholderOrOpeningQuote( + $statement, + $tokenOffset, + $fragmentOffset, + $fragments, + $currentLiteralDelimiter, + $paramMap + ); + } else { + $result = self::findClosingQuote($statement, $tokenOffset, $currentLiteralDelimiter); + } + } while ($result); + + if ($currentLiteralDelimiter) { + throw new OCI8Exception(sprintf( + 'The statement contains non-terminated string literal starting at offset %d', + $tokenOffset - 1 + )); + } + + $fragments[] = substr($statement, $fragmentOffset); + $statement = implode('', $fragments); + + return [$statement, $paramMap]; + } + + /** + * Finds next placeholder or opening quote. + * + * @param string $statement The SQL statement to parse + * @param string $tokenOffset The offset to start searching from + * @param int $fragmentOffset The offset to build the next fragment from + * @param string[] $fragments Fragments of the original statement not containing placeholders + * @param string|null $currentLiteralDelimiter The delimiter of the current string literal + * or NULL if not currently in a literal + * @param array $paramMap Mapping of the original parameter positions to their named replacements + * @return bool Whether the token was found + */ + private static function findPlaceholderOrOpeningQuote( + $statement, + &$tokenOffset, + &$fragmentOffset, + &$fragments, + &$currentLiteralDelimiter, + &$paramMap + ) { + $token = self::findToken($statement, $tokenOffset, '/[?\'"]/'); + + if (!$token) { + return false; + } + + if ($token === '?') { + $position = count($paramMap) + 1; + $param = ':param' . $position; + $fragments[] = substr($statement, $fragmentOffset, $tokenOffset - $fragmentOffset); + $fragments[] = $param; + $paramMap[$position] = $param; + $tokenOffset += 1; + $fragmentOffset = $tokenOffset; + + return true; + } + + $currentLiteralDelimiter = $token; + ++$tokenOffset; + + return true; + } + + /** + * Finds closing quote + * + * @param string $statement The SQL statement to parse + * @param string $tokenOffset The offset to start searching from + * @param string|null $currentLiteralDelimiter The delimiter of the current string literal + * or NULL if not currently in a literal + * @return bool Whether the token was found + */ + private static function findClosingQuote( + $statement, + &$tokenOffset, + &$currentLiteralDelimiter + ) { + $token = self::findToken( + $statement, + $tokenOffset, + '/' . preg_quote($currentLiteralDelimiter, '/') . '/' + ); + + if (!$token) { + return false; + } + + $currentLiteralDelimiter = false; + ++$tokenOffset; + + return true; + } + + /** + * Finds the token described by regex starting from the given offset. Updates the offset with the position + * where the token was found. + * + * @param string $statement The SQL statement to parse + * @param string $offset The offset to start searching from + * @param string $regex The regex containing token pattern + * @return string|null Token or NULL if not found + */ + private static function findToken($statement, &$offset, $regex) + { + if (preg_match($regex, $statement, $matches, PREG_OFFSET_CAPTURE, $offset)) { + $offset = $matches[0][1]; + return $matches[0][0]; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $column = isset($this->_paramMap[$column]) ? $this->_paramMap[$column] : $column; + + if ($type == \PDO::PARAM_LOB) { + $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); + + $this->boundValues[$column] =& $lob; + + return oci_bind_by_name($this->_sth, $column, $lob, -1, OCI_B_BLOB); + } elseif ($length !== null) { + $this->boundValues[$column] =& $variable; + + return oci_bind_by_name($this->_sth, $column, $variable, $length); + } + + $this->boundValues[$column] =& $variable; + + return oci_bind_by_name($this->_sth, $column, $variable); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + // not having the result means there's nothing to close + if (!$this->result) { + return true; + } + + oci_cancel($this->_sth); + + $this->result = false; + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return oci_num_fields($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->_sth); + if ($error !== false) { + $error = $error['code']; + } + + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + if ($hasZeroIndex && is_numeric($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode()); + if ( ! $ret) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + + $this->result = true; + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return false; + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + if (PDO::FETCH_OBJ == $fetchMode) { + return oci_fetch_object($this->_sth); + } + + if (! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + return oci_fetch_array( + $this->_sth, + self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS + ); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + $result = []; + + if (PDO::FETCH_OBJ == $fetchMode) { + while ($row = $this->fetch($fetchMode)) { + $result[] = $row; + } + + return $result; + } + + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) { + while ($row = $this->fetch($fetchMode)) { + $result[] = $row; + } + } else { + $fetchStructure = OCI_FETCHSTATEMENT_BY_ROW; + if ($fetchMode == PDO::FETCH_COLUMN) { + $fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN; + } + + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return []; + } + + oci_fetch_all($this->_sth, $result, 0, -1, + self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS); + + if ($fetchMode == PDO::FETCH_COLUMN) { + $result = $result[0]; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return false; + } + + $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return oci_num_rows($this->_sth); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOConnection.php b/wbce/include/Doctrine/DBAL/Driver/PDOConnection.php new file mode 100644 index 000000000..cb47c3e9a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use PDO; + +/** + * PDO implementation of the Connection interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection +{ + /** + * @param string $dsn + * @param string|null $user + * @param string|null $password + * @param array|null $options + * + * @throws PDOException in case of an error. + */ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + try { + parent::__construct($dsn, $user, $password, $options); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['Doctrine\DBAL\Driver\PDOStatement', []]); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + try { + return parent::exec($statement); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return PDO::getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString, $driverOptions = []) + { + try { + return parent::prepare($prepareString, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $argsCount = count($args); + + try { + if ($argsCount == 4) { + return parent::query($args[0], $args[1], $args[2], $args[3]); + } + + if ($argsCount == 3) { + return parent::query($args[0], $args[1], $args[2]); + } + + if ($argsCount == 2) { + return parent::query($args[0], $args[1]); + } + + return parent::query($args[0]); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + return parent::quote($input, $type); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return parent::lastInsertId($name); + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOException.php b/wbce/include/Doctrine/DBAL/Driver/PDOException.php new file mode 100644 index 000000000..7a1a02a43 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Tiny wrapper for PDOException instances to implement the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PDOException extends \PDOException implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param \PDOException $exception The PDO exception to wrap. + */ + public function __construct(\PDOException $exception) + { + parent::__construct($exception->getMessage(), 0, $exception); + + $this->code = $exception->getCode(); + $this->errorInfo = $exception->errorInfo; + $this->errorCode = isset($exception->errorInfo[1]) ? $exception->errorInfo[1] : $exception->getCode(); + $this->sqlState = isset($exception->errorInfo[0]) ? $exception->errorInfo[0] : $exception->getCode(); + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOIbm/Driver.php new file mode 100644 index 000000000..faebcd842 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Driver for the PDO IBM extension. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + $conn = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * Constructs the IBM PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'ibm:'; + if (isset($params['host'])) { + $dsn .= 'HOSTNAME=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'PORT=' . $params['port'] . ';'; + } + $dsn .= 'PROTOCOL=TCPIP;'; + if (isset($params['dbname'])) { + $dsn .= 'DATABASE=' . $params['dbname'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_ibm'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOMySql/Driver.php new file mode 100644 index 000000000..44a9d5b82 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + $conn = new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + if (isset($params['charset'])) { + $dsn .= 'charset=' . $params['charset'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_mysql'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOOracle/Driver.php new file mode 100644 index 000000000..61edca747 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * PDO Oracle driver. + * + * WARNING: This driver gives us segfaults in our testsuites on CLOB and other + * stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community, + * which leads us to the recommendation to use the "oci8" driver to connect + * to Oracle instead. + */ +class Driver extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + return new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (\PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function constructPdoDsn(array $params) + { + $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_oracle'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOPgSql/Driver.php new file mode 100644 index 000000000..fa993e7a5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOPgSql; + +use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\DBALException; +use PDOException; +use PDO; + +/** + * Driver that connects through pdo_pgsql. + * + * @since 2.0 + */ +class Driver extends AbstractPostgreSQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + if (defined('PDO::PGSQL_ATTR_DISABLE_PREPARES') + && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || true === $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] + ) + ) { + $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + } + + /* defining client_encoding via SET NAMES to avoid inconsistent DSN support + * - the 'client_encoding' connection param only works with postgres >= 9.1 + * - passing client_encoding via the 'options' param breaks pgbouncer support + */ + if (isset($params['charset'])) { + $pdo->query('SET NAMES \''.$params['charset'].'\''); + } + + return $pdo; + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Postgres PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + + if (isset($params['port']) && $params['port'] != '') { + $dsn .= 'port=' . $params['port'] . ';'; + } + + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } elseif (isset($params['default_dbname'])) { + $dsn .= 'dbname=' . $params['default_dbname'] . ';'; + } else { + // Used for temporary connections to allow operations like dropping the database currently connected to. + // Connecting without an explicit database does not work, therefore "postgres" database is used + // as it is mostly present in every server setup. + $dsn .= 'dbname=postgres' . ';'; + } + + if (isset($params['sslmode'])) { + $dsn .= 'sslmode=' . $params['sslmode'] . ';'; + } + + if (isset($params['sslrootcert'])) { + $dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';'; + } + + if (isset($params['sslcert'])) { + $dsn .= 'sslcert=' . $params['sslcert'] . ';'; + } + + if (isset($params['sslkey'])) { + $dsn .= 'sslkey=' . $params['sslkey'] . ';'; + } + + if (isset($params['sslcrl'])) { + $dsn .= 'sslcrl=' . $params['sslcrl'] . ';'; + } + + if (isset($params['application_name'])) { + $dsn .= 'application_name=' . $params['application_name'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_pgsql'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOSqlite/Driver.php new file mode 100644 index 000000000..4fe4e7201 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLiteDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLiteDriver +{ + /** + * @var array + */ + protected $_userDefinedFunctions = [ + 'sqrt' => ['callback' => ['Doctrine\DBAL\Platforms\SqlitePlatform', 'udfSqrt'], 'numArgs' => 1], + 'mod' => ['callback' => ['Doctrine\DBAL\Platforms\SqlitePlatform', 'udfMod'], 'numArgs' => 2], + 'locate' => ['callback' => ['Doctrine\DBAL\Platforms\SqlitePlatform', 'udfLocate'], 'numArgs' => -1], + ]; + + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + if (isset($driverOptions['userDefinedFunctions'])) { + $this->_userDefinedFunctions = array_merge( + $this->_userDefinedFunctions, $driverOptions['userDefinedFunctions']); + unset($driverOptions['userDefinedFunctions']); + } + + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $ex) { + throw DBALException::driverException($this, $ex); + } + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } elseif (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlite'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php new file mode 100644 index 000000000..8c561555c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends PDOConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * {@inheritdoc} + */ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + parent::__construct($dsn, $user, $password, $options); + $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if (null === $name) { + return parent::lastInsertId($name); + } + + $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); + $stmt->execute([$name]); + + return $stmt->fetchColumn(); + } + + /** + * {@inheritDoc} + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + $val = parent::quote($value, $type); + + // Fix for a driver version terminating all values with null byte + if (strpos($val, "\0") !== false) { + $val = substr($val, 0, -1); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php new file mode 100644 index 000000000..d65bcd690 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + return new Connection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Sqlsrv PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + } + + if (isset($params['port']) && !empty($params['port'])) { + $dsn .= ',' . $params['port']; + } + + if (isset($params['dbname'])) { + $dsn .= ';Database=' . $params['dbname']; + } + + if (isset($params['MultipleActiveResultSets'])) { + $dsn .= '; MultipleActiveResultSets=' . ($params['MultipleActiveResultSets'] ? 'true' : 'false'); + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlsrv'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php new file mode 100644 index 000000000..3d1b22a65 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\PDOStatement; +use PDO; + +/** + * PDO SQL Server Statement + */ +class Statement extends PDOStatement +{ + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = PDO::PARAM_STR, $length = null, $driverOptions = null) + { + if ($type === PDO::PARAM_LOB && $driverOptions === null) { + $driverOptions = PDO::SQLSRV_ENCODING_BINARY; + } + + return parent::bindParam($column, $variable, $type, $length, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = PDO::PARAM_STR) + { + return $this->bindParam($param, $value, $type); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PDOStatement.php b/wbce/include/Doctrine/DBAL/Driver/PDOStatement.php new file mode 100644 index 000000000..deb35de01 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,170 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * The PDO implementation of the Statement interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOStatement extends \PDOStatement implements Statement +{ + /** + * Protected constructor. + */ + protected function __construct() + { + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + // This thin wrapper is necessary to shield against the weird signature + // of PDOStatement::setFetchMode(): even if the second and third + // parameters are optional, PHP will not let us remove it from this + // declaration. + try { + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchMode); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchMode, $arg2); + } + + return parent::setFetchMode($fetchMode, $arg2, $arg3); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = \PDO::PARAM_STR) + { + try { + return parent::bindValue($param, $value, $type); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = \PDO::PARAM_STR, $length = null, $driverOptions = null) + { + try { + return parent::bindParam($column, $variable, $type, $length, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + try { + return parent::closeCursor(); + } catch (\PDOException $exception) { + // Exceptions not allowed by the interface. + // In case driver implementations do not adhere to the interface, silence exceptions here. + return true; + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + try { + return parent::execute($params); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + try { + if ($fetchMode === null && \PDO::FETCH_ORI_NEXT === $cursorOrientation && 0 === $cursorOffset) { + return parent::fetch(); + } + + if (\PDO::FETCH_ORI_NEXT === $cursorOrientation && 0 === $cursorOffset) { + return parent::fetch($fetchMode); + } + + if (0 === $cursorOffset) { + return parent::fetch($fetchMode, $cursorOrientation); + } + + return parent::fetch($fetchMode, $cursorOrientation, $cursorOffset); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + try { + if ($fetchMode === null && null === $fetchArgument && null === $ctorArgs) { + return parent::fetchAll(); + } + + if (null === $fetchArgument && null === $ctorArgs) { + return parent::fetchAll($fetchMode); + } + + if (null === $ctorArgs) { + return parent::fetchAll($fetchMode, $fetchArgument); + } + + return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + try { + return parent::fetchColumn($columnIndex); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/PingableConnection.php b/wbce/include/Doctrine/DBAL/Driver/PingableConnection.php new file mode 100644 index 000000000..8c85a82c8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/PingableConnection.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * An interface for connections which support a "native" ping method. + * + * @link www.doctrine-project.org + * @since 2.5 + * @author Till Klampaeckel + * @author Benjamin Eberlei + */ +interface PingableConnection +{ + /** + * Pings the database server to determine if the connection is still + * available. Return true/false based on if that was successful or not. + * + * @return bool + */ + public function ping(); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/ResultStatement.php b/wbce/include/Doctrine/DBAL/Driver/ResultStatement.php new file mode 100644 index 000000000..b73c2cdae --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Interface for the reading part of a prepare statement only. + * + * @author Benjamin Eberlei + */ +interface ResultStatement extends \Traversable +{ + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean TRUE on success or FALSE on failure. + */ + public function closeCursor(); + + /** + * Returns the number of columns in the result set + * + * @return integer The number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + public function columnCount(); + + /** + * Sets the fetch mode to use while iterating this statement. + * + * @param integer $fetchMode The fetch mode must be one of the PDO::FETCH_* constants. + * @param mixed $arg2 + * @param mixed $arg3 + * + * @return boolean + * + * @see PDO::FETCH_* constants. + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + + /** + * Returns the next row of a result set. + * + * @param int|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the \PDO::FETCH_* constants, + * defaulting to \PDO::FETCH_BOTH. + * @param int $cursorOrientation For a ResultStatement object representing a scrollable cursor, + * this value determines which row will be returned to the caller. + * This value must be one of the \PDO::FETCH_ORI_* constants, + * defaulting to \PDO::FETCH_ORI_NEXT. To request a scrollable + * cursor for your ResultStatement object, you must set the \PDO::ATTR_CURSOR + * attribute to \PDO::CURSOR_SCROLL when you prepare the SQL statement with + * \PDO::prepare(). + * @param int $cursorOffset For a ResultStatement object representing a scrollable cursor for which the + * cursorOrientation parameter is set to \PDO::FETCH_ORI_ABS, this value + * specifies the absolute number of the row in the result set that shall be + * fetched. + * For a ResultStatement object representing a scrollable cursor for which the + * cursorOrientation parameter is set to \PDO::FETCH_ORI_REL, this value + * specifies the row to fetch relative to the cursor position before + * ResultStatement::fetch() was called. + * + * @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is + * returned on failure. + * + * @see PDO::FETCH_* constants. + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0); + + /** + * Returns an array containing all of the result set rows. + * + * @param int|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the \PDO::FETCH_* constants, + * defaulting to \PDO::FETCH_BOTH. + * @param int|null $fetchArgument This argument has a different meaning depending on the value of the $fetchMode parameter: + * * \PDO::FETCH_COLUMN: Returns the indicated 0-indexed column. + * * \PDO::FETCH_CLASS: Returns instances of the specified class, mapping the columns of each + * row to named properties in the class. + * * \PDO::FETCH_FUNC: Returns the results of calling the specified function, using each row's + * columns as parameters in the call. + * @param array|null $ctorArgs Controls how the next row will be returned to the caller. + * The value must be one of the \PDO::FETCH_* constants, + * defaulting to \PDO::FETCH_BOTH. + * + * @return array + * + * @see \PDO::FETCH_* constants. + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null); + + /** + * Returns a single column from the next row of a result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. + * If no value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string|boolean A single column in the next row of a result set, or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php new file mode 100644 index 000000000..c0f6249d4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver; + +/** + * A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class Driver extends AbstractSQLAnywhereDriver +{ + /** + * {@inheritdoc} + * + * @throws \Doctrine\DBAL\DBALException if there was a problem establishing the connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + try { + return new SQLAnywhereConnection( + $this->buildDsn( + isset($params['host']) ? $params['host'] : null, + isset($params['port']) ? $params['port'] : null, + isset($params['server']) ? $params['server'] : null, + isset($params['dbname']) ? $params['dbname'] : null, + $username, + $password, + $driverOptions + ), + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (SQLAnywhereException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Build the connection string for given connection parameters and driver options. + * + * @param string $host Host address to connect to. + * @param integer $port Port to use for the connection (default to SQL Anywhere standard port 2638). + * @param string $server Database server name on the host to connect to. + * SQL Anywhere allows multiple database server instances on the same host, + * therefore specifying the server instance name to use is mandatory. + * @param string $dbname Name of the database on the server instance to connect to. + * @param string $username User name to use for connection authentication. + * @param string $password Password to use for connection authentication. + * @param array $driverOptions Additional parameters to use for the connection. + * + * @return string + */ + private function buildDsn($host, $port, $server, $dbname, $username = null, $password = null, array $driverOptions = []) + { + $host = $host ?: 'localhost'; + $port = $port ?: 2638; + + if (! empty($server)) { + $server = ';ServerName=' . $server; + } + + return + 'HOST=' . $host . ':' . $port . + $server . + ';DBN=' . $dbname . + ';UID=' . $username . + ';PWD=' . $password . + ';' . implode( + ';', + array_map(function ($key, $value) { + return $key . '=' . $value; + }, array_keys($driverOptions), $driverOptions) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php new file mode 100644 index 000000000..b7777f66f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php @@ -0,0 +1,223 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SAP Sybase SQL Anywhere implementation of the Connection interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource The SQL Anywhere connection resource. + */ + private $connection; + + /** + * Constructor. + * + * Connects to database with given connection string. + * + * @param string $dsn The connection string. + * @param boolean $persistent Whether or not to establish a persistent connection. + * + * @throws SQLAnywhereException + */ + public function __construct($dsn, $persistent = false) + { + $this->connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn); + + if ( ! is_resource($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError(); + } + + // Disable PHP warnings on error. + if ( ! sasql_set_option($this->connection, 'verbose_errors', false)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable auto committing by default. + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable exact, non-approximated row count retrieval. + if ( ! sasql_set_option($this->connection, 'row_counts', true)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function beginTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'off')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function commit() + { + if ( ! sasql_commit($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_errorcode($this->connection); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_error($this->connection); + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + if (false === sasql_real_query($this->connection, $statement)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return sasql_affected_rows($this->connection); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return $this->query("SELECT PROPERTY('ProductVersion')")->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if (null === $name) { + return sasql_insert_id($this->connection); + } + + return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new SQLAnywhereStatement($this->connection, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $stmt = $this->prepare($args[0]); + + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + if (is_int($input) || is_float($input)) { + return $input; + } + + return "'" . sasql_escape_string($this->connection, $input) . "'"; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function rollBack() + { + if ( ! sasql_rollback($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * Ends transactional mode and enables auto commit again. + * + * @throws SQLAnywhereException + * + * @return boolean Whether or not ending transactional mode succeeded. + */ + private function endTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php new file mode 100644 index 000000000..046938037 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * SAP Sybase SQL Anywhere driver exception. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereException extends AbstractDriverException +{ + /** + * Helper method to turn SQL Anywhere error into exception. + * + * @param resource|null $conn The SQL Anywhere connection resource to retrieve the last error from. + * @param resource|null $stmt The SQL Anywhere statement resource to retrieve the last error from. + * + * @return SQLAnywhereException + * + * @throws \InvalidArgumentException + */ + public static function fromSQLAnywhereError($conn = null, $stmt = null) + { + if (null !== $conn && ! (is_resource($conn))) { + throw new \InvalidArgumentException('Invalid SQL Anywhere connection resource given: ' . $conn); + } + + if (null !== $stmt && ! (is_resource($stmt))) { + throw new \InvalidArgumentException('Invalid SQL Anywhere statement resource given: ' . $stmt); + } + + $state = $conn ? sasql_sqlstate($conn) : sasql_sqlstate(); + $code = null; + $message = null; + + /** + * Try retrieving the last error from statement resource if given + */ + if ($stmt) { + $code = sasql_stmt_errno($stmt); + $message = sasql_stmt_error($stmt); + } + + /** + * Try retrieving the last error from the connection resource + * if either the statement resource is not given or the statement + * resource is given but the last error could not be retrieved from it (fallback). + * Depending on the type of error, it is sometimes necessary to retrieve + * it from the connection resource even though it occurred during + * a prepared statement. + */ + if ($conn && ! $code) { + $code = sasql_errorcode($conn); + $message = sasql_error($conn); + } + + /** + * Fallback mode if either no connection resource is given + * or the last error could not be retrieved from the given + * connection / statement resource. + */ + if ( ! $conn || ! $code) { + $code = sasql_errorcode(); + $message = sasql_error(); + } + + if ($message) { + return new self('SQLSTATE [' . $state . '] [' . $code . '] ' . $message, $state, $code); + } + + return new self('SQL Anywhere error occurred but no error message was retrieved from driver.', $state, $code); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php new file mode 100644 index 000000000..aadf02eb7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -0,0 +1,347 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use IteratorAggregate; +use PDO; +use Doctrine\DBAL\Driver\Statement; + +/** + * SAP SQL Anywhere implementation of the Statement interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereStatement implements IteratorAggregate, Statement +{ + /** + * @var resource The connection resource. + */ + private $conn; + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = []; + + /** + * @var int Default fetch mode to use. + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var resource The result set resource to fetch. + */ + private $result; + + /** + * @var resource The prepared SQL statement to execute. + */ + private $stmt; + + /** + * Constructor. + * + * Prepares given statement for given connection. + * + * @param resource $conn The connection resource to use. + * @param string $sql The SQL statement to prepare. + * + * @throws SQLAnywhereException + */ + public function __construct($conn, $sql) + { + if ( ! is_resource($conn)) { + throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn); + } + + $this->conn = $conn; + $this->stmt = sasql_prepare($conn, $sql); + + if ( ! is_resource($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($conn); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + switch ($type) { + case PDO::PARAM_INT: + case PDO::PARAM_BOOL: + $type = 'i'; + break; + case PDO::PARAM_LOB: + $type = 'b'; + break; + case PDO::PARAM_NULL: + case PDO::PARAM_STR: + $type = 's'; + break; + default: + throw new SQLAnywhereException('Unknown type: ' . $type); + } + + if ( ! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function closeCursor() + { + if (!sasql_stmt_reset($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sasql_stmt_field_count($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_stmt_errno($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_stmt_error($this->stmt); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function execute($params = null) + { + if (is_array($params)) { + $hasZeroIndex = array_key_exists(0, $params); + + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + + $this->bindValue($key, $val); + } + } + + if ( ! sasql_stmt_execute($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + $this->result = sasql_stmt_result_metadata($this->stmt); + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + if ( ! is_resource($this->result)) { + return false; + } + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_ASSOC: + return sasql_fetch_assoc($this->result); + case PDO::FETCH_BOTH: + return sasql_fetch_array($this->result, SASQL_BOTH); + case PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : []; + } + + $result = sasql_fetch_object($this->result); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case PDO::FETCH_NUM: + return sasql_fetch_row($this->result); + case PDO::FETCH_OBJ: + return sasql_fetch_object($this->result); + default: + throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $rows = []; + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->fetchAll()); + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sasql_stmt_affected_rows($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws SQLAnywhereException + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = []) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new SQLAnywhereException(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + } else { + $destinationClass->$name = $value; + } + } + + return $destinationClass; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLSrv/Driver.php b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/Driver.php new file mode 100644 index 000000000..da0409f24 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/Driver.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * Driver for ext/sqlsrv. + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) + { + if (!isset($params['host'])) { + throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver."); + } + + $serverName = $params['host']; + if (isset($params['port'])) { + $serverName .= ', ' . $params['port']; + } + + if (isset($params['dbname'])) { + $driverOptions['Database'] = $params['dbname']; + } + + if (isset($params['charset'])) { + $driverOptions['CharacterSet'] = $params['charset']; + } + + $driverOptions['UID'] = $username; + $driverOptions['PWD'] = $password; + + if (!isset($driverOptions['ReturnDatesAsStrings'])) { + $driverOptions['ReturnDatesAsStrings'] = 1; + } + + return new SQLSrvConnection($serverName, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlsrv'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php new file mode 100644 index 000000000..67c7bfa85 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Last Id Data Container. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class LastInsertId +{ + /** + * @var integer + */ + private $id; + + /** + * @param integer $id + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php new file mode 100644 index 000000000..6de138a4a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -0,0 +1,195 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SQL Server implementation for the Connection interface. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $conn; + + /** + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId + */ + protected $lastInsertId; + + /** + * @param string $serverName + * @param array $connectionOptions + * + * @throws \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + public function __construct($serverName, $connectionOptions) + { + if ( ! sqlsrv_configure('WarningsReturnAsErrors', 0)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + $this->conn = sqlsrv_connect($serverName, $connectionOptions); + if ( ! $this->conn) { + throw SQLSrvException::fromSqlSrvErrors(); + } + $this->lastInsertId = new LastInsertId(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = sqlsrv_server_info($this->conn); + + return $serverInfo['SQLServerVersion']; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function prepare($sql) + { + return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritDoc} + * @license New BSD, code from Zend Framework + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value)) { + return $value; + } elseif (is_float($value)) { + return sprintf('%F', $value); + } + + return "'" . str_replace("'", "''", $value) . "'"; + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $stmt = sqlsrv_query($this->conn, $statement); + + if (false === $stmt) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + return sqlsrv_rows_affected($stmt); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name !== null) { + $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); + $stmt->execute([$name]); + + return $stmt->fetchColumn(); + } + + return $this->lastInsertId->getId(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + if ( ! sqlsrv_begin_transaction($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + if ( ! sqlsrv_commit($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + if ( ! sqlsrv_rollback($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php new file mode 100644 index 000000000..b2c463259 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + + +use Doctrine\DBAL\Driver\AbstractDriverException; + +class SQLSrvException extends AbstractDriverException +{ + /** + * Helper method to turn sql server errors into exception. + * + * @return \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + static public function fromSqlSrvErrors() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + $message = ""; + $sqlState = null; + $errorCode = null; + + foreach ($errors as $error) { + $message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n"; + + if (null === $sqlState) { + $sqlState = $error['SQLSTATE']; + } + + if (null === $errorCode) { + $errorCode = $error['code']; + } + } + if ( ! $message) { + $message = "SQL Server error occurred but no error message was retrieved from driver."; + } + + return new self(rtrim($message), $sqlState, $errorCode); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php new file mode 100644 index 000000000..412cbc707 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -0,0 +1,386 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * SQL Server Statement. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvStatement implements IteratorAggregate, Statement +{ + /** + * The SQLSRV Resource. + * + * @var resource + */ + private $conn; + + /** + * The SQL statement to execute. + * + * @var string + */ + private $sql; + + /** + * The SQLSRV statement resource. + * + * @var resource + */ + private $stmt; + + /** + * References to the variables bound as statement parameters. + * + * @var array + */ + private $variables = []; + + /** + * Bound parameter types. + * + * @var array + */ + private $types = []; + + /** + * Translations. + * + * @var array + */ + private static $fetchMap = [ + PDO::FETCH_BOTH => SQLSRV_FETCH_BOTH, + PDO::FETCH_ASSOC => SQLSRV_FETCH_ASSOC, + PDO::FETCH_NUM => SQLSRV_FETCH_NUMERIC, + ]; + + /** + * The name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClass = '\stdClass'; + + /** + * The constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClassCtorArgs = []; + + /** + * The fetch style. + * + * @param integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * The last insert ID. + * + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null + */ + private $lastInsertId; + + /** + * Indicates whether the statement is in the state when fetching results is possible + * + * @var bool + */ + private $result = false; + + /** + * Append to any INSERT query to retrieve the last insert id. + * + * @var string + */ + const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; + + /** + * @param resource $conn + * @param string $sql + * @param \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null $lastInsertId + */ + public function __construct($conn, $sql, LastInsertId $lastInsertId = null) + { + $this->conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') === 0) { + $this->sql .= self::LAST_INSERT_ID_SQL; + $this->lastInsertId = $lastInsertId; + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + if (!is_numeric($param)) { + throw new SQLSrvException( + 'sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.' + ); + } + + $this->variables[$param] = $value; + $this->types[$param] = $type; + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (!is_numeric($column)) { + throw new SQLSrvException("sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead."); + } + + $this->variables[$column] =& $variable; + $this->types[$column] = $type; + + // unset the statement resource if it exists as the new one will need to be bound to the new variable + $this->stmt = null; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + // not having the result means there's nothing to close + if (!$this->result) { + return true; + } + + // emulate it by fetching and discarding rows, similarly to what PDO does in this case + // @link http://php.net/manual/en/pdostatement.closecursor.php + // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 + // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them + while (sqlsrv_fetch($this->stmt)); + + $this->result = false; + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sqlsrv_num_fields($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + $this->bindValue($key, $val); + } + } + + if ( ! $this->stmt) { + $this->stmt = $this->prepare(); + } + + if (!sqlsrv_execute($this->stmt)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + if ($this->lastInsertId) { + sqlsrv_next_result($this->stmt); + sqlsrv_fetch($this->stmt); + $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0)); + } + + $this->result = true; + } + + /** + * Prepares SQL Server statement resource + * + * @return resource + * @throws SQLSrvException + */ + private function prepare() + { + $params = []; + + foreach ($this->variables as $column => &$variable) { + if (PDO::PARAM_LOB === $this->types[$column]) { + $params[$column - 1] = [ + &$variable, + SQLSRV_PARAM_IN, + SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_VARBINARY('max'), + ]; + } else { + $params[$column - 1] =& $variable; + } + } + + $stmt = sqlsrv_prepare($this->conn, $this->sql, $params); + + if (!$stmt) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + * + * @throws SQLSrvException + */ + public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + // do not try fetching from the statement if it's not expected to contain result + // in order to prevent exceptional situation + if (!$this->result) { + return false; + } + + $args = func_get_args(); + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if (isset(self::$fetchMap[$fetchMode])) { + return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?: false; + } + + if (in_array($fetchMode, [PDO::FETCH_OBJ, PDO::FETCH_CLASS], true)) { + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (count($args) >= 2) { + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : []; + } + + return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false; + } + + throw new SQLSrvException('Fetch mode is not supported!'); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $rows = []; + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array([$this, 'fetch'], func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sqlsrv_rows_affected($this->stmt); + } +} diff --git a/wbce/include/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php b/wbce/include/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php new file mode 100644 index 000000000..f15ffefcb --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a connection that is able to provide information about the server it is connected to. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ServerInfoAwareConnection +{ + /** + * Returns the version number of the database server connected to. + * + * @return string + */ + public function getServerVersion(); + + /** + * Checks whether a query is required to retrieve the database server version. + * + * @return boolean True if a query is required to retrieve the database server version, false otherwise. + */ + public function requiresQueryForServerVersion(); +} diff --git a/wbce/include/Doctrine/DBAL/Driver/Statement.php b/wbce/include/Doctrine/DBAL/Driver/Statement.php new file mode 100644 index 000000000..aedf47b8a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named (not supported by mysqli driver, see comment below) or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter. + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + + /** + * Binds a PHP variable to a corresponding named (not supported by mysqli driver, see comment below) or question + * mark placeholder in the SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that PDOStatement->execute() is called. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $column Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement using + * question mark placeholders, this will be the 1-indexed position of the parameter. + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * @param integer|null $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * @param integer|null $length You must specify maxlength when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null, $length = null); + + /** + * Fetches the SQLSTATE associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorCode() + * + * @return string The error code string. + */ + function errorCode(); + + /** + * Fetches extended error information associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorInfo() + * + * @return array The error info array. + */ + function errorInfo(); + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values. + * + * + * @param array|null $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer The number of rows. + */ + function rowCount(); +} diff --git a/wbce/include/Doctrine/DBAL/DriverManager.php b/wbce/include/Doctrine/DBAL/DriverManager.php new file mode 100644 index 000000000..e3488df1e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,427 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\Common\EventManager; + +/** + * Factory for creating Doctrine\DBAL\Connection instances. + * + * @author Roman Borschel + * @since 2.0 + */ +final class DriverManager +{ + /** + * List of supported drivers and their mappings to the driver classes. + * + * To add your own driver use the 'driverClass' parameter to + * {@link DriverManager::getConnection()}. + * + * @var array + */ + private static $_driverMap = array( + 'pdo_mysql' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', + 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', + 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', + 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', + 'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', + 'sqlanywhere' => 'Doctrine\DBAL\Driver\SQLAnywhere\Driver', + 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + ); + + /** + * List of URL schemes from a database URL and their mappings to driver. + */ + private static $driverSchemeAliases = array( + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ); + + /** + * Private constructor. This class cannot be instantiated. + */ + private function __construct() + { + } + + /** + * Creates a connection object based on the specified parameters. + * This method returns a Doctrine\DBAL\Connection which wraps the underlying + * driver connection. + * + * $params must contain at least one of the following. + * + * Either 'driver' with one of the following values: + * + * pdo_mysql + * pdo_sqlite + * pdo_pgsql + * pdo_oci (unstable) + * pdo_sqlsrv + * pdo_sqlsrv + * mysqli + * sqlanywhere + * sqlsrv + * ibm_db2 (unstable) + * drizzle_pdo_mysql + * + * OR 'driverClass' that contains the full class name (with namespace) of the + * driver class to instantiate. + * + * Other (optional) parameters: + * + * user (string): + * The username to use when connecting. + * + * password (string): + * The password to use when connecting. + * + * driverOptions (array): + * Any additional driver-specific options for the driver. These are just passed + * through to the driver. + * + * pdo: + * You can pass an existing PDO instance through this parameter. The PDO + * instance will be wrapped in a Doctrine\DBAL\Connection. + * + * wrapperClass: + * You may specify a custom wrapper class through the 'wrapperClass' + * parameter but this class MUST inherit from Doctrine\DBAL\Connection. + * + * driverClass: + * The driver class to use. + * + * @param array $params The parameters. + * @param \Doctrine\DBAL\Configuration|null $config The configuration to use. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager to use. + * + * @return \Doctrine\DBAL\Connection + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function getConnection( + array $params, + Configuration $config = null, + EventManager $eventManager = null) + { + // create default config and event manager, if not set + if ( ! $config) { + $config = new Configuration(); + } + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $params = self::parseDatabaseUrl($params); + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } elseif (isset($params['pdo'])) { + $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + self::_checkParams($params); + } + if (isset($params['driverClass'])) { + $className = $params['driverClass']; + } else { + $className = self::$_driverMap[$params['driver']]; + } + + $driver = new $className(); + + $wrapperClass = 'Doctrine\DBAL\Connection'; + if (isset($params['wrapperClass'])) { + if (is_subclass_of($params['wrapperClass'], $wrapperClass)) { + $wrapperClass = $params['wrapperClass']; + } else { + throw DBALException::invalidWrapperClass($params['wrapperClass']); + } + } + + return new $wrapperClass($params, $driver, $config, $eventManager); + } + + /** + * Returns the list of supported drivers. + * + * @return array + */ + public static function getAvailableDrivers() + { + return array_keys(self::$_driverMap); + } + + /** + * Checks the list of parameters. + * + * @param array $params The list of parameters. + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + private static function _checkParams(array $params) + { + // check existence of mandatory parameters + + // driver + if ( ! isset($params['driver']) && ! isset($params['driverClass'])) { + throw DBALException::driverRequired(); + } + + // check validity of parameters + + // driver + if (isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) { + throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap)); + } + + if (isset($params['driverClass']) && ! in_array('Doctrine\DBAL\Driver', class_implements($params['driverClass'], true))) { + throw DBALException::invalidDriverClass($params['driverClass']); + } + } + + /** + * Normalizes the given connection URL path. + * + * @param string $urlPath + * + * @return string The normalized connection URL path + */ + private static function normalizeDatabaseUrlPath($urlPath) + { + // Trim leading slash from URL path. + return substr($urlPath, 1); + } + + /** + * Extracts parts from a database URL, if present, and returns an + * updated list of parameters. + * + * @param array $params The list of parameters. + * + * @return array A modified list of parameters with info from a database + * URL extracted into indidivual parameter parts. + * + * @throws DBALException + */ + private static function parseDatabaseUrl(array $params) + { + if (!isset($params['url'])) { + return $params; + } + + // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid + $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']); + + // PHP < 5.4.8 doesn't parse schemeless urls properly. + // See: https://php.net/parse-url#refsect1-function.parse-url-changelog + if (PHP_VERSION_ID < 50408 && strpos($url, '//') === 0) { + $url = parse_url('fake:' . $url); + + unset($url['scheme']); + } else { + $url = parse_url($url); + } + + if ($url === false) { + throw new DBALException('Malformed parameter "url".'); + } + + $url = array_map('rawurldecode', $url); + + // If we have a connection URL, we have to unset the default PDO instance connection parameter (if any) + // as we cannot merge connection details from the URL into the PDO instance (URL takes precedence). + unset($params['pdo']); + + $params = self::parseDatabaseUrlScheme($url, $params); + + if (isset($url['host'])) { + $params['host'] = $url['host']; + } + if (isset($url['port'])) { + $params['port'] = $url['port']; + } + if (isset($url['user'])) { + $params['user'] = $url['user']; + } + if (isset($url['pass'])) { + $params['password'] = $url['pass']; + } + + $params = self::parseDatabaseUrlPath($url, $params); + $params = self::parseDatabaseUrlQuery($url, $params); + + return $params; + } + + /** + * Parses the given connection URL and resolves the given connection parameters. + * + * Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters + * via {@link parseDatabaseUrlScheme}. + * + * @param array $url The URL parts to evaluate. + * @param array $params The connection parameters to resolve. + * + * @return array The resolved connection parameters. + * + * @see parseDatabaseUrlScheme + */ + private static function parseDatabaseUrlPath(array $url, array $params) + { + if (! isset($url['path'])) { + return $params; + } + + $url['path'] = self::normalizeDatabaseUrlPath($url['path']); + + // If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate + // and therefore treat the path as regular DBAL connection URL path. + if (! isset($params['driver'])) { + return self::parseRegularDatabaseUrlPath($url, $params); + } + + if (strpos($params['driver'], 'sqlite') !== false) { + return self::parseSqliteDatabaseUrlPath($url, $params); + } + + return self::parseRegularDatabaseUrlPath($url, $params); + } + + /** + * Parses the query part of the given connection URL and resolves the given connection parameters. + * + * @param array $url The connection URL parts to evaluate. + * @param array $params The connection parameters to resolve. + * + * @return array The resolved connection parameters. + */ + private static function parseDatabaseUrlQuery(array $url, array $params) + { + if (! isset($url['query'])) { + return $params; + } + + $query = array(); + + parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode + + return array_merge($params, $query); // parse_str wipes existing array elements + } + + /** + * Parses the given regular connection URL and resolves the given connection parameters. + * + * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}. + * + * @param array $url The regular connection URL parts to evaluate. + * @param array $params The connection parameters to resolve. + * + * @return array The resolved connection parameters. + * + * @see normalizeDatabaseUrlPath + */ + private static function parseRegularDatabaseUrlPath(array $url, array $params) + { + $params['dbname'] = $url['path']; + + return $params; + } + + /** + * Parses the given SQLite connection URL and resolves the given connection parameters. + * + * Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}. + * + * @param array $url The SQLite connection URL parts to evaluate. + * @param array $params The connection parameters to resolve. + * + * @return array The resolved connection parameters. + * + * @see normalizeDatabaseUrlPath + */ + private static function parseSqliteDatabaseUrlPath(array $url, array $params) + { + if ($url['path'] === ':memory:') { + $params['memory'] = true; + + return $params; + } + + $params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key + + return $params; + } + + /** + * Parses the scheme part from given connection URL and resolves the given connection parameters. + * + * @param array $url The connection URL parts to evaluate. + * @param array $params The connection parameters to resolve. + * + * @return array The resolved connection parameters. + * + * @throws DBALException if parsing failed or resolution is not possible. + */ + private static function parseDatabaseUrlScheme(array $url, array $params) + { + if (isset($url['scheme'])) { + // The requested driver from the URL scheme takes precedence + // over the default custom driver from the connection parameters (if any). + unset($params['driverClass']); + + // URL schemes must not contain underscores, but dashes are ok + $driver = str_replace('-', '_', $url['scheme']); + + // The requested driver from the URL scheme takes precedence + // over the default driver from the connection parameters (if any). + $params['driver'] = isset(self::$driverSchemeAliases[$driver]) + // use alias like "postgres", else we just let checkParams decide later + // if the driver exists (for literal "pdo-pgsql" etc) + ? self::$driverSchemeAliases[$driver] + : $driver; + + return $params; + } + + // If a schemeless connection URL is given, we require a default driver or default custom driver + // as connection parameter. + if (! isset($params['driverClass']) && ! isset($params['driver'])) { + throw DBALException::driverRequired($params['url']); + } + + return $params; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/ConnectionEventArgs.php b/wbce/include/Doctrine/DBAL/Event/ConnectionEventArgs.php new file mode 100644 index 000000000..fd82638e7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_connection->getDriver(); + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } + + /** + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + return $this->_connection->getSchemaManager(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/wbce/include/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php new file mode 100644 index 000000000..bd362c4cb --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * MySQL Session Init Event Subscriber which allows to set the Client Encoding of the Connection. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * The charset. + * + * @var string + */ + private $_charset; + + /** + * The collation, or FALSE if no collation. + * + * @var string|boolean + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection. + * + * @param string $charset The charset. + * @param string|boolean $collation The collation, or FALSE if no collation. + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/wbce/include/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php new file mode 100644 index 000000000..1c7ddec2f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Should be used when Oracle Server default environment does not match the Doctrine requirements. + * + * The following environment variables are required for the Doctrine default date format: + * + * NLS_TIME_FORMAT="HH24:MI:SS" + * NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + /** + * @var array + */ + protected $_defaultSessionVars = array( + 'NLS_TIME_FORMAT' => "HH24:MI:SS", + 'NLS_DATE_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_TZ_FORMAT' => "YYYY-MM-DD HH24:MI:SS TZH:TZM", + 'NLS_NUMERIC_CHARACTERS' => ".,", + ); + + /** + * @param array $oracleSessionVars + */ + public function __construct(array $oracleSessionVars = array()) + { + $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + if (count($this->_defaultSessionVars)) { + array_change_key_case($this->_defaultSessionVars, \CASE_UPPER); + $vars = array(); + foreach ($this->_defaultSessionVars as $option => $value) { + if ($option === 'CURRENT_SCHEMA') { + $vars[] = $option . " = " . $value; + } else { + $vars[] = $option . " = '" . $value . "'"; + } + } + $sql = "ALTER SESSION SET ".implode(" ", $vars); + $args->getConnection()->executeUpdate($sql); + } + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php b/wbce/include/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php new file mode 100644 index 000000000..cfa42994c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Session init listener for executing a single SQL statement right after a connection is opened. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + */ +class SQLSessionInit implements EventSubscriber +{ + /** + * @var string + */ + protected $sql; + + /** + * @param string $sql + */ + public function __construct($sql) + { + $this->sql = $sql; + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php new file mode 100644 index 000000000..c4477ad8a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php new file mode 100644 index 000000000..7249b63cb --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_columnDiff = $columnDiff; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\ColumnDiff + */ + public function getColumnDiff() + { + return $this->_columnDiff; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php new file mode 100644 index 000000000..384315d5a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php new file mode 100644 index 000000000..06f37888f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php new file mode 100644 index 000000000..94a274c4f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_oldColumnName = $oldColumnName; + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return string + */ + public function getOldColumnName() + { + return $this->_oldColumnName; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php new file mode 100644 index 000000000..14bb8df73 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column|null + */ + private $_column = null; + + /** + * Raw column data as fetched from the database. + * + * @var array + */ + private $_tableColumn; + + /** + * @var string + */ + private $_table; + + /** + * @var string + */ + private $_database; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(array $tableColumn, $table, $database, Connection $connection) + { + $this->_tableColumn = $tableColumn; + $this->_table = $table; + $this->_database = $database; + $this->_connection = $connection; + } + + /** + * Allows to clear the column which means the column will be excluded from + * tables column list. + * + * @param null|\Doctrine\DBAL\Schema\Column $column + * + * @return \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column|null + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return array + */ + public function getTableColumn() + { + return $this->_tableColumn; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->_database; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php new file mode 100644 index 000000000..38b218257 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, Table $table, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php new file mode 100644 index 000000000..175b0e9c3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var array + */ + private $_columns; + + /** + * @var array + */ + private $_options; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param array $columns + * @param array $options + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Table $table, array $columns, array $options, AbstractPlatform $platform) + { + $this->_table = $table; + $this->_columns = $columns; + $this->_options = $options; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php new file mode 100644 index 000000000..6ffb6442c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var string|null + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @throws \InvalidArgumentException + */ + public function __construct($table, AbstractPlatform $platform) + { + if ( ! $table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaDropTableEventArgs expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return string|\Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string $sql + * + * @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs + */ + public function setSql($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * @return string|null + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaEventArgs.php new file mode 100644 index 000000000..d9648b64f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaEventArgs extends EventArgs +{ + /** + * @var boolean + */ + private $_preventDefault = false; + + /** + * @return \Doctrine\DBAL\Event\SchemaEventArgs + */ + public function preventDefault() + { + $this->_preventDefault = true; + + return $this; + } + + /** + * @return boolean + */ + public function isDefaultPrevented() + { + return $this->_preventDefault; + } +} diff --git a/wbce/include/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php b/wbce/include/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php new file mode 100644 index 000000000..3fd1d4bf7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index|null + */ + private $_index = null; + + /** + * Raw index data as fetched from the database. + * + * @var array + */ + private $_tableIndex; + + /** + * @var string + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(array $tableIndex, $table, Connection $connection) + { + $this->_tableIndex = $tableIndex; + $this->_table = $table; + $this->_connection = $connection; + } + + /** + * Allows to clear the index which means the index will be excluded from tables index list. + * + * @param null|\Doctrine\DBAL\Schema\Index $index + * + * @return SchemaIndexDefinitionEventArgs + */ + public function setIndex(Index $index = null) + { + $this->_index = $index; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Index|null + */ + public function getIndex() + { + return $this->_index; + } + + /** + * @return array + */ + public function getTableIndex() + { + return $this->_tableIndex; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Events.php b/wbce/include/Doctrine/DBAL/Events.php new file mode 100644 index 000000000..0d31504c7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Events.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class cannot be instantiated. + */ + private function __construct() + { + } + + const postConnect = 'postConnect'; + + const onSchemaCreateTable = 'onSchemaCreateTable'; + const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; + const onSchemaDropTable = 'onSchemaDropTable'; + const onSchemaAlterTable = 'onSchemaAlterTable'; + const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn'; + const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn'; + const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; + const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; + const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; +} diff --git a/wbce/include/Doctrine/DBAL/Exception/ConnectionException.php b/wbce/include/Doctrine/DBAL/Exception/ConnectionException.php new file mode 100644 index 000000000..8d976dcb3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/ConnectionException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all connection related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConnectionException extends DriverException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/ConstraintViolationException.php b/wbce/include/Doctrine/DBAL/Exception/ConstraintViolationException.php new file mode 100644 index 000000000..8dd1cb694 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/ConstraintViolationException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all constraint violation related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConstraintViolationException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php b/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php new file mode 100644 index 000000000..279aae9a5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all already existing database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectExistsException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php b/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php new file mode 100644 index 000000000..fbca22d99 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all unknown database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectNotFoundException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/DeadlockException.php b/wbce/include/Doctrine/DBAL/Exception/DeadlockException.php new file mode 100644 index 000000000..05af11e4b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/DeadlockException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a deadlock error of a transaction detected in the driver. + * + * @author Tobias Schultze + * @link www.doctrine-project.org + * @since 2.6 + */ +class DeadlockException extends ServerException implements RetryableException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/DriverException.php b/wbce/include/Doctrine/DBAL/Exception/DriverException.php new file mode 100644 index 000000000..17c9f3a84 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/DriverException.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Base class for all errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DriverException extends DBALException +{ + /** + * The previous DBAL driver exception. + * + * @var \Doctrine\DBAL\Driver\DriverException + */ + private $driverException; + + /** + * Constructor. + * + * @param string $message The exception message. + * @param \Doctrine\DBAL\Driver\DriverException $driverException The DBAL driver exception to chain. + */ + public function __construct($message, \Doctrine\DBAL\Driver\DriverException $driverException) + { + $exception = null; + + if ($driverException instanceof \Exception) { + $exception = $driverException; + } + + parent::__construct($message, 0, $exception); + + $this->driverException = $driverException; + } + + /** + * Returns the driver specific error code if given. + * + * Returns null if no error code was given by the driver. + * + * @return integer|string|null + */ + public function getErrorCode() + { + return $this->driverException->getErrorCode(); + } + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred, if given. + * + * Returns null if no SQLSTATE was given by the driver. + * + * @return string|null + */ + public function getSQLState() + { + return $this->driverException->getSQLState(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php b/wbce/include/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php new file mode 100644 index 000000000..d244855a1 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a foreign key constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ForeignKeyConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/InvalidArgumentException.php b/wbce/include/Doctrine/DBAL/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..5b811227b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/InvalidArgumentException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Exception to be thrown when invalid arguments are passed to any DBAL API + * + * @author Marco Pivetta + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidArgumentException extends DBALException +{ + /** + * @return self + */ + public static function fromEmptyCriteria() + { + return new self('Empty criteria was used, expected non-empty criteria'); + } +} diff --git a/wbce/include/Doctrine/DBAL/Exception/InvalidFieldNameException.php b/wbce/include/Doctrine/DBAL/Exception/InvalidFieldNameException.php new file mode 100644 index 000000000..e0b1013fe --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/InvalidFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an invalid specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidFieldNameException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/LockWaitTimeoutException.php b/wbce/include/Doctrine/DBAL/Exception/LockWaitTimeoutException.php new file mode 100644 index 000000000..915e11b8b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/LockWaitTimeoutException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a lock wait timeout error of a transaction detected in the driver. + * + * @author Tobias Schultze + * @link www.doctrine-project.org + * @since 2.6 + */ +class LockWaitTimeoutException extends ServerException implements RetryableException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php b/wbce/include/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php new file mode 100644 index 000000000..7b214e3b9 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a non-unique/ambiguous specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NonUniqueFieldNameException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php b/wbce/include/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php new file mode 100644 index 000000000..a1f45b92a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a NOT NULL constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NotNullConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/ReadOnlyException.php b/wbce/include/Doctrine/DBAL/Exception/ReadOnlyException.php new file mode 100644 index 000000000..0345e88fc --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/ReadOnlyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a write operation attempt on a read-only database element detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ReadOnlyException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/RetryableException.php b/wbce/include/Doctrine/DBAL/Exception/RetryableException.php new file mode 100644 index 000000000..6b019dcc4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/RetryableException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Marker interface for all exceptions where retrying the transaction makes sense. + * + * @author Tobias Schultze + * @link www.doctrine-project.org + * @since 2.6 + */ +interface RetryableException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/ServerException.php b/wbce/include/Doctrine/DBAL/Exception/ServerException.php new file mode 100644 index 000000000..36460ee0d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/ServerException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all server related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ServerException extends DriverException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/SyntaxErrorException.php b/wbce/include/Doctrine/DBAL/Exception/SyntaxErrorException.php new file mode 100644 index 000000000..97b8fd25f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/SyntaxErrorException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a syntax error in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SyntaxErrorException extends ServerException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/TableExistsException.php b/wbce/include/Doctrine/DBAL/Exception/TableExistsException.php new file mode 100644 index 000000000..c90bfb476 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/TableExistsException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an already existing table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableExistsException extends DatabaseObjectExistsException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/TableNotFoundException.php b/wbce/include/Doctrine/DBAL/Exception/TableNotFoundException.php new file mode 100644 index 000000000..90793b909 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/TableNotFoundException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an unknown table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableNotFoundException extends DatabaseObjectNotFoundException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php b/wbce/include/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php new file mode 100644 index 000000000..cd117738d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a unique constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class UniqueConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/wbce/include/Doctrine/DBAL/Id/TableGenerator.php b/wbce/include/Doctrine/DBAL/Id/TableGenerator.php new file mode 100644 index 000000000..abf393eca --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Id/TableGenerator.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Connection; + +/** + * Table ID Generator for those poor languages that are missing sequences. + * + * WARNING: The Table Id Generator clones a second independent database + * connection to work correctly. This means using the generator requests that + * generate IDs will have two open database connections. This is necessary to + * be safe from transaction failures in the main connection. Make sure to only + * ever use one TableGenerator otherwise you end up with many connections. + * + * TableID Generator does not work with SQLite. + * + * The TableGenerator does not take care of creating the SQL Table itself. You + * should look at the `TableGeneratorSchemaVisitor` to do this for you. + * Otherwise the schema for a table looks like: + * + * CREATE sequences ( + * sequence_name VARCHAR(255) NOT NULL, + * sequence_value INT NOT NULL DEFAULT 1, + * sequence_increment_by INT NOT NULL DEFAULT 1, + * PRIMARY KEY (sequence_name) + * ); + * + * Technically this generator works as follows: + * + * 1. Use a robust transaction serialization level. + * 2. Open transaction + * 3. Acquire a read lock on the table row (SELECT .. FOR UPDATE) + * 4. Increment current value by one and write back to database + * 5. Commit transaction + * + * If you are using a sequence_increment_by value that is larger than one the + * ID Generator will keep incrementing values until it hits the incrementation + * gap before issuing another query. + * + * If no row is present for a given sequence a new one will be created with the + * default values 'value' = 1 and 'increment_by' = 1 + * + * @author Benjamin Eberlei + */ +class TableGenerator +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $generatorTableName; + + /** + * @var array + */ + private $sequences = array(); + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param string $generatorTableName + * + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(Connection $conn, $generatorTableName = 'sequences') + { + $params = $conn->getParams(); + if ($params['driver'] == 'pdo_sqlite') { + throw new \Doctrine\DBAL\DBALException("Cannot use TableGenerator with SQLite."); + } + $this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager()); + $this->generatorTableName = $generatorTableName; + } + + /** + * Generates the next unused value for the given sequence name. + * + * @param string $sequenceName + * + * @return integer + * + * @throws \Doctrine\DBAL\DBALException + */ + public function nextValue($sequenceName) + { + if (isset($this->sequences[$sequenceName])) { + $value = $this->sequences[$sequenceName]['value']; + $this->sequences[$sequenceName]['value']++; + if ($this->sequences[$sequenceName]['value'] >= $this->sequences[$sequenceName]['max']) { + unset ($this->sequences[$sequenceName]); + } + + return $value; + } + + $this->conn->beginTransaction(); + + try { + $platform = $this->conn->getDatabasePlatform(); + $sql = "SELECT sequence_value, sequence_increment_by " . + "FROM " . $platform->appendLockHint($this->generatorTableName, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) . " " . + "WHERE sequence_name = ? " . $platform->getWriteLockSQL(); + $stmt = $this->conn->executeQuery($sql, array($sequenceName)); + + if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row = array_change_key_case($row, CASE_LOWER); + + $value = $row['sequence_value']; + $value++; + + if ($row['sequence_increment_by'] > 1) { + $this->sequences[$sequenceName] = array( + 'value' => $value, + 'max' => $row['sequence_value'] + $row['sequence_increment_by'] + ); + } + + $sql = "UPDATE " . $this->generatorTableName . " ". + "SET sequence_value = sequence_value + sequence_increment_by " . + "WHERE sequence_name = ? AND sequence_value = ?"; + $rows = $this->conn->executeUpdate($sql, array($sequenceName, $row['sequence_value'])); + + if ($rows != 1) { + throw new \Doctrine\DBAL\DBALException("Race-condition detected while updating sequence. Aborting generation"); + } + } else { + $this->conn->insert( + $this->generatorTableName, + array('sequence_name' => $sequenceName, 'sequence_value' => 1, 'sequence_increment_by' => 1) + ); + $value = 1; + } + + $this->conn->commit(); + + } catch (\Exception $e) { + $this->conn->rollBack(); + throw new \Doctrine\DBAL\DBALException("Error occurred while generating ID with TableGenerator, aborted generation: " . $e->getMessage(), 0, $e); + } + + return $value; + } +} diff --git a/wbce/include/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php b/wbce/include/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php new file mode 100644 index 000000000..25b06aca9 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class TableGeneratorSchemaVisitor implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + /** + * @var string + */ + private $generatorTableName; + + /** + * @param string $generatorTableName + */ + public function __construct($generatorTableName = 'sequences') + { + $this->generatorTableName = $generatorTableName; + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $table = $schema->createTable($this->generatorTableName); + $table->addColumn('sequence_name', 'string'); + $table->addColumn('sequence_value', 'integer', array('default' => 1)); + $table->addColumn('sequence_increment_by', 'integer', array('default' => 1)); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/wbce/include/Doctrine/DBAL/LockMode.php b/wbce/include/Doctrine/DBAL/LockMode.php new file mode 100644 index 000000000..8d78180ed --- /dev/null +++ b/wbce/include/Doctrine/DBAL/LockMode.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + /** + * Private constructor. This class cannot be instantiated. + */ + final private function __construct() + { + } +} diff --git a/wbce/include/Doctrine/DBAL/Logging/DebugStack.php b/wbce/include/Doctrine/DBAL/Logging/DebugStack.php new file mode 100644 index 000000000..b143a3ee2 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** + * Executed SQL queries. + * + * @var array + */ + public $queries = array(); + + /** + * If Debug Stack is enabled (log queries) or not. + * + * @var boolean + */ + public $enabled = true; + + /** + * @var float|null + */ + public $start = null; + + /** + * @var integer + */ + public $currentQuery = 0; + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if ($this->enabled) { + $this->start = microtime(true); + $this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + if ($this->enabled) { + $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Logging/EchoSQLLogger.php b/wbce/include/Doctrine/DBAL/Logging/EchoSQLLogger.php new file mode 100644 index 000000000..8ed04788f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * A SQL logger that logs to the standard output using echo/var_dump. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EchoSQLLogger implements SQLLogger +{ + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + echo $sql . PHP_EOL; + + if ($params) { + var_dump($params); + } + + if ($types) { + var_dump($types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + } +} diff --git a/wbce/include/Doctrine/DBAL/Logging/LoggerChain.php b/wbce/include/Doctrine/DBAL/Logging/LoggerChain.php new file mode 100644 index 000000000..81845b1b2 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + /** + * @var \Doctrine\DBAL\Logging\SQLLogger[] + */ + private $loggers = array(); + + /** + * Adds a logger in the chain. + * + * @param \Doctrine\DBAL\Logging\SQLLogger $logger + * + * @return void + */ + public function addLogger(SQLLogger $logger) + { + $this->loggers[] = $logger; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + foreach ($this->loggers as $logger) { + $logger->startQuery($sql, $params, $types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + foreach ($this->loggers as $logger) { + $logger->stopQuery(); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Logging/SQLLogger.php b/wbce/include/Doctrine/DBAL/Logging/SQLLogger.php new file mode 100644 index 000000000..52491e50f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface SQLLogger +{ + /** + * Logs a SQL statement somewhere. + * + * @param string $sql The SQL to be executed. + * @param array|null $params The SQL parameters. + * @param array|null $types The SQL parameter types. + * + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Marks the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery(); +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/AbstractPlatform.php b/wbce/include/Doctrine/DBAL/Platforms/AbstractPlatform.php new file mode 100644 index 000000000..f9d54b0f8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,3573 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Types; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\SchemaCreateTableEventArgs; +use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs; +use Doctrine\DBAL\Event\SchemaDropTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs; + +/** + * Base class for all DatabasePlatforms. The DatabasePlatforms are the central + * point of abstraction of platform-specific behaviors, features and SQL dialects. + * They are a passive source of information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Remove any unnecessary methods. + */ +abstract class AbstractPlatform +{ + /** + * @var integer + */ + const CREATE_INDEXES = 1; + + /** + * @var integer + */ + const CREATE_FOREIGNKEYS = 2; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_SECOND = 'SECOND'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MINUTE = 'MINUTE'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_HOUR = 'HOUR'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_DAY = 'DAY'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_WEEK = 'WEEK'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MONTH = 'MONTH'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_QUARTER = 'QUARTER'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_YEAR = 'YEAR'; + + /** + * @var integer + */ + const TRIM_UNSPECIFIED = 0; + + /** + * @var integer + */ + const TRIM_LEADING = 1; + + /** + * @var integer + */ + const TRIM_TRAILING = 2; + + /** + * @var integer + */ + const TRIM_BOTH = 3; + + /** + * @var array|null + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array|null + */ + protected $doctrineTypeComments = null; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * Holds the KeywordList instance for the current platform. + * + * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + protected $_keywords; + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Sets the EventManager used by the Platform. + * + * @param \Doctrine\Common\EventManager $eventManager + */ + public function setEventManager(EventManager $eventManager) + { + $this->_eventManager = $eventManager; + } + + /** + * Gets the EventManager used by the Platform. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Returns the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Lazy load Doctrine Type Mappings. + * + * @return void + */ + abstract protected function initializeDoctrineTypeMappings(); + + /** + * Initializes Doctrine Type Mappings with the platform defaults + * and with all additional type mappings. + * + * @return void + */ + private function initializeAllDoctrineTypeMappings() + { + $this->initializeDoctrineTypeMappings(); + + foreach (Type::getTypesMap() as $typeName => $className) { + foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { + $this->doctrineTypeMapping[$dbType] = $typeName; + } + } + } + + /** + * Returns the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + * + * @return string + */ + public function getVarcharTypeDeclarationSQL(array $field) + { + if ( !isset($field['length'])) { + $field['length'] = $this->getVarcharDefaultLength(); + } + + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + if ($field['length'] > $this->getVarcharMaxLength()) { + return $this->getClobTypeDeclarationSQL($field); + } + + return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param array $field The column definition. + * + * @return string + */ + public function getBinaryTypeDeclarationSQL(array $field) + { + if ( ! isset($field['length'])) { + $field['length'] = $this->getBinaryDefaultLength(); + } + + $fixed = isset($field['fixed']) ? $field['fixed'] : false; + + if ($field['length'] > $this->getBinaryMaxLength()) { + return $this->getBlobTypeDeclarationSQL($field); + } + + return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet to declare a GUID/UUID field. + * + * By default this maps directly to a CHAR(36) and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getGuidTypeDeclarationSQL(array $field) + { + $field['length'] = 36; + $field['fixed'] = true; + + return $this->getVarcharTypeDeclarationSQL($field); + } + + /** + * Returns the SQL snippet to declare a JSON field. + * + * By default this maps directly to a CLOB and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return $this->getClobTypeDeclarationSQL($field); + } + + /** + * @param integer $length + * @param boolean $fixed + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param integer $length The length of the column. + * @param boolean $fixed Whether the column length is fixed. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); + } + + /** + * Returns the SQL snippet used to declare a CLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Returns the SQL Snippet used to declare a BLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getBlobTypeDeclarationSQL(array $field); + + /** + * Gets the name of the platform. + * + * @return string + */ + abstract public function getName(); + + /** + * Registers a doctrine type to be used in conjunction with a column type of this platform. + * + * @param string $dbType + * @param string $doctrineType + * + * @throws \Doctrine\DBAL\DBALException If the type is not found. + */ + public function registerDoctrineTypeMapping($dbType, $doctrineType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + if (!Types\Type::hasType($doctrineType)) { + throw DBALException::typeNotFound($doctrineType); + } + + $dbType = strtolower($dbType); + $this->doctrineTypeMapping[$dbType] = $doctrineType; + + $doctrineType = Type::getType($doctrineType); + + if ($doctrineType->requiresSQLCommentHint($this)) { + $this->markDoctrineTypeCommented($doctrineType); + } + } + + /** + * Gets the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException + */ + public function getDoctrineTypeMapping($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + if (!isset($this->doctrineTypeMapping[$dbType])) { + throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); + } + + return $this->doctrineTypeMapping[$dbType]; + } + + /** + * Checks if a database type is currently supported by this platform. + * + * @param string $dbType + * + * @return boolean + */ + public function hasDoctrineTypeMappingFor($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + return isset($this->doctrineTypeMapping[$dbType]); + } + + /** + * Initializes the Doctrine Type comments instance variable for in_array() checks. + * + * @return void + */ + protected function initializeCommentedDoctrineTypes() + { + $this->doctrineTypeComments = array(); + + foreach (Type::getTypesMap() as $typeName => $className) { + $type = Type::getType($typeName); + + if ($type->requiresSQLCommentHint($this)) { + $this->doctrineTypeComments[] = $typeName; + } + } + } + + /** + * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return boolean + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param string|\Doctrine\DBAL\Types\Type $doctrineType + * + * @return void + */ + public function markDoctrineTypeCommented($doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + } + + /** + * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Gets the comment of a passed column modified by potential doctrine type comment hints. + * + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return string + */ + protected function getColumnComment(Column $column) + { + $comment = $column->getComment(); + + if ($this->isCommentedDoctrineType($column->getType())) { + $comment .= $this->getDoctrineTypeComment($column->getType()); + } + + return $comment; + } + + /** + * Gets the character used for identifier quoting. + * + * @return string + */ + public function getIdentifierQuoteCharacter() + { + return '"'; + } + + /** + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentStartString() + { + return "--"; + } + + /** + * Gets the string portion that ends an SQL comment. + * + * @return string + */ + public function getSqlCommentEndString() + { + return "\n"; + } + + /** + * Gets the maximum length of a varchar field. + * + * @return integer + */ + public function getVarcharMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a varchar field. + * + * @return integer + */ + public function getVarcharDefaultLength() + { + return 255; + } + + /** + * Gets the maximum length of a binary field. + * + * @return integer + */ + public function getBinaryMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a binary field. + * + * @return integer + */ + public function getBinaryDefaultLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the global unique identifier expression. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get the average value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including an AVG aggregate function. + */ + public function getAvgExpression($column) + { + return 'AVG(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the number of rows (without a NULL value) of a column. + * + * If a '*' is used instead of a column the number of selected rows is returned. + * + * @param string|integer $column The column to use. + * + * @return string Generated SQL including a COUNT aggregate function. + */ + public function getCountExpression($column) + { + return 'COUNT(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the highest value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a MAX aggregate function. + */ + public function getMaxExpression($column) + { + return 'MAX(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the lowest value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a MIN aggregate function. + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the total sum of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a SUM aggregate function. + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the SQL snippet to get the md5 sum of a field. + * + * Note: Not SQL92, but common functionality. + * + * @param string $column + * + * @return string + */ + public function getMd5Expression($column) + { + return 'MD5(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the length of a text field. + * + * @param string $column + * + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the squared value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including an SQRT aggregate function. + */ + public function getSqrtExpression($column) + { + return 'SQRT(' . $column . ')'; + } + + /** + * Returns the SQL snippet to round a numeric field to the number of decimals specified. + * + * @param string $column + * @param integer $decimals + * + * @return string + */ + public function getRoundExpression($column, $decimals = 0) + { + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * + * @return string + */ + public function getModExpression($expression1, $expression2) + { + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + + /** + * Returns the SQL snippet to trim a string. + * + * @param string $str The expression to apply the trim to. + * @param integer $pos The position of the trim (leading/trailing/both). + * @param string|boolean $char The char to trim, has to be quoted already. Defaults to space. + * + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $expression = ''; + + switch ($pos) { + case self::TRIM_LEADING: + $expression = 'LEADING '; + break; + + case self::TRIM_TRAILING: + $expression = 'TRAILING '; + break; + + case self::TRIM_BOTH: + $expression = 'BOTH '; + break; + } + + if (false !== $char) { + $expression .= $char . ' '; + } + + if ($pos || false !== $char) { + $expression .= 'FROM '; + } + + return 'TRIM(' . $expression . $str . ')'; + } + + /** + * Returns the SQL snippet to trim trailing space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to trim leading space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression to uppercase, + * according to the current character set mapping. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getUpperExpression($str) + { + return 'UPPER(' . $str . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression to lowercase, + * according to the current character set mapping. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getLowerExpression($str) + { + return 'LOWER(' . $str . ')'; + } + + /** + * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. + * + * @param string $str Literal string. + * @param string $substr Literal string to find. + * @param integer|boolean $startPos Position to start at, beginning of string by default. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * Returns a SQL snippet to get a substring inside an SQL statement. + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function. + * + * @param string $value An sql string literal or column name/alias. + * @param integer $from Where to start the substring portion. + * @param integer|null $length The substring portion length. + * + * @return string + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * Returns a SQL snippet to concatenate the given expressions. + * + * Accepts an arbitrary number of string parameters. Each parameter must contain an expression. + * + * @return string + */ + public function getConcatExpression() + { + return join(' || ', func_get_args()); + } + + /** + * Returns the SQL for a logical not. + * + * Example: + * + * $q = new Doctrine_Query(); + * $e = $q->expr; + * $q->select('*')->from('table') + * ->where($e->eq('id', $e->not('null')); + * + * + * @param string $expression + * + * @return string The logical expression. + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL that checks if an expression is null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns the SQL that checks if an expression is not null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns the SQL that checks if an expression evaluates to a value between two values. + * + * The parameter $expression is checked if it is between $value1 and $value2. + * + * Note: There is a slight difference in the way BETWEEN works on some databases. + * http://www.w3schools.com/sql/sql_between.asp. If you want complete database + * independence you should avoid using between(). + * + * @param string $expression The value to compare to. + * @param string $value1 The lower value to compare with. + * @param string $value2 The higher value to compare with. + * + * @return string The logical expression. + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + /** + * Returns the SQL to get the arccosine of a value. + * + * @param string $value + * + * @return string + */ + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + /** + * Returns the SQL to get the sine of a value. + * + * @param string $value + * + * @return string + */ + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + /** + * Returns the SQL to get the PI value. + * + * @return string + */ + public function getPiExpression() + { + return 'PI()'; + } + + /** + * Returns the SQL to get the cosine of a value. + * + * @param string $value + * + * @return string + */ + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Returns the SQL to calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2. + * + * @param string $date1 + * @param string $date2 + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to add the number of given seconds to a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to subtract the number of given seconds from a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to add the number of given minutes to a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to subtract the number of given minutes from a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to add the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to subtract the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to add the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to subtract the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to add the number of given weeks to a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to subtract the number of given weeks from a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to add the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to subtract the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to add the number of given quarters to a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to subtract the number of given quarters from a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to add the number of given years to a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL to subtract the number of given years from a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL for a date arithmetic expression. + * + * @param string $date The column or literal representing a date to perform the arithmetic operation on. + * @param string $operator The arithmetic operator (+ or -). + * @param integer $interval The interval that shall be calculated into the date. + * @param string $unit The unit of the interval that shall be calculated into the date. + * One of the DATE_INTERVAL_UNIT_* constants. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL bit AND comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Returns the SQL bit OR comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + /** + * Returns the FOR UPDATE expression. + * + * @return string + */ + public function getForUpdateSQL() + { + return 'FOR UPDATE'; + } + + /** + * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification. + * + * @param string $fromClause The FROM clause to append the hint for the given lock mode to. + * @param integer|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will + * be appended to the FROM clause. + * + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database + * vendors allow to lighten this constraint up to be a real read lock. + * + * @return string + */ + public function getReadLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. + * + * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Returns the SQL snippet to drop an existing database. + * + * @param string $database The name of the database that should be dropped. + * + * @return string + */ + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Returns the SQL snippet to drop an existing table. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { + $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return $eventArgs->getSql(); + } + } + + return 'DROP TABLE ' . $table; + } + + /** + * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Returns the SQL to drop an index from a table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Returns the SQL to drop a constraint. + * + * @param \Doctrine\DBAL\Schema\Constraint|string $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropConstraintSQL($constraint, $table) + { + if (! $constraint instanceof Constraint) { + $constraint = new Identifier($constraint); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $constraint = $constraint->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + } + + /** + * Returns the SQL to drop a foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if (! $foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = new Identifier($foreignKey); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $foreignKey = $foreignKey->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; + } + + /** + * Returns the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param \Doctrine\DBAL\Schema\Table $table + * @param integer $createFlags + * + * @return array The sequence of SQL statements. + * + * @throws \Doctrine\DBAL\DBALException + * @throws \InvalidArgumentException + */ + public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) + { + if ( ! is_int($createFlags)) { + throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer."); + } + + if (count($table->getColumns()) === 0) { + throw DBALException::noColumnsSpecifiedForTable($table->getName()); + } + + $tableName = $table->getQuotedName($this); + $options = $table->getOptions(); + $options['uniqueConstraints'] = array(); + $options['indexes'] = array(); + $options['primary'] = array(); + + if (($createFlags&self::CREATE_INDEXES) > 0) { + foreach ($table->getIndexes() as $index) { + /* @var $index Index */ + if ($index->isPrimary()) { + $options['primary'] = $index->getQuotedColumns($this); + $options['primary_index'] = $index; + } else { + $options['indexes'][$index->getQuotedName($this)] = $index; + } + } + } + + $columnSql = array(); + $columns = array(); + + foreach ($table->getColumns() as $column) { + /* @var \Doctrine\DBAL\Schema\Column $column */ + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { + $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + if ($eventArgs->isDefaultPrevented()) { + continue; + } + } + + $columnData = $column->toArray(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; + $columnData['comment'] = $this->getColumnComment($column); + + if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + + if (in_array($column->getName(), $options['primary'])) { + $columnData['primary'] = true; + } + + $columns[$columnData['name']] = $columnData; + } + + if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = array(); + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { + $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return array_merge($eventArgs->getSql(), $columnSql); + } + } + + $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + if ($this->supportsCommentOnStatement()) { + foreach ($table->getColumns() as $column) { + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); + } + } + } + + return array_merge($sql, $columnSql); + } + + /** + * @param string $tableName + * @param string $columnName + * @param string $comment + * + * @return string + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS " . $comment; + } + + /** + * Returns the SQL to create inline comment on a column. + * + * @param string $comment + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getInlineColumnCommentSQL($comment) + { + if (! $this->supportsInlineColumnComments()) { + throw DBALException::notSupported(__METHOD__); + } + + return "COMMENT " . $this->quoteStringLiteral($comment); + } + + /** + * Returns the SQL used to create a table. + * + * @param string $tableName + * @param array $columns + * @param array $options + * + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if ( ! empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * @return string + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Returns the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a constraint on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')'; + + $referencesClause = ''; + if ($constraint instanceof Index) { + if ($constraint->isPrimary()) { + $query .= ' PRIMARY KEY'; + } elseif ($constraint->isUnique()) { + $query .= ' UNIQUE'; + } else { + throw new \InvalidArgumentException( + 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' + ); + } + } elseif ($constraint instanceof ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . + ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Returns the SQL to create an index on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getQuotedColumns($this); + + if (count($columns) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index); + + return $query; + } + + /** + * Adds condition for partial index. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getPartialIndexSQL(Index $index) + { + if ($this->supportsPartialIndexes() && $index->hasOption('where')) { + return ' WHERE ' . $index->getOption('where'); + } + + return ''; + } + + /** + * Adds additional flags for index generation. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getCreateIndexSQLFlags(Index $index) + { + return $index->isUnique() ? 'UNIQUE ' : ''; + } + + /** + * Returns the SQL to create an unnamed primary key constraint. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL to create a named schema. + * + * @param string $schemaName + * + * @return string + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSchemaSQL($schemaName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Quotes a string so that it can be safely used as a table or column name, + * even if it is a reserved word of the platform. This also detects identifier + * chains separated by dot and quotes them independently. + * + * NOTE: Just because you CAN use quoted identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteSingleIdentifier"), explode(".", $str)); + + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quotes a single identifier (no dot chain separation). + * + * @param string $str The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Returns the SQL to create a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return string + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); + + return $query; + } + + /** + * Gets the SQL statements for altering an existing table. + * + * This method returns an array of SQL statements, since some platforms need several statements. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $sql + * + * @return boolean + */ + protected function onSchemaAlterTable(TableDiff $diff, &$sql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { + return false; + } + + $eventArgs = new SchemaAlterTableEventArgs($diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); + + $sql = array_merge($sql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->getName($this)->getQuotedName($this); + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->removedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->removedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + + return $sql; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + $sql = array(); + + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->addedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->addedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + foreach ($diff->renamedIndexes as $oldIndexName => $index) { + $oldIndexName = new Identifier($oldIndexName); + $sql = array_merge( + $sql, + $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName) + ); + } + + return $sql; + } + + /** + * Returns the SQL for renaming an index on a table. + * + * @param string $oldIndexName The name of the index to rename from. + * @param \Doctrine\DBAL\Schema\Index $index The definition of the index to rename to. + * @param string $tableName The table to rename the given index on. + * + * @return array The sequence of SQL statements for renaming the given index. + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + $this->getDropIndexSQL($oldIndexName, $tableName), + $this->getCreateIndexSQL($index, $tableName) + ); + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Gets declaration of a number of fields in bulk. + * + * @param array $fields A multidimensional associative array. + * The first dimension determines the field name, while the second + * dimension is keyed with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * + * @return string + */ + public function getColumnDeclarationListSQL(array $fields) + { + $queryFields = array(); + + foreach ($fields as $fieldName => $field) { + $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field); + } + + return implode(', ', $queryFields); + } + + /** + * Obtains DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name The name the field to be declared. + * @param array $field An associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * check + * column check constraint + * columnDefinition + * a string that defines the complete column + * + * @return string DBMS specific SQL code portion that should be used to declare the column. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $charset = (isset($field['charset']) && $field['charset']) ? + ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : ''; + + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSQLDeclaration($field, $this); + $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; + + if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') { + $columnDef .= ' ' . $this->getInlineColumnCommentSQL($field['comment']); + } + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns the SQL snippet that declares a floating point column of arbitrary precision. + * + * @param array $columnDef + * + * @return string + */ + public function getDecimalTypeDeclarationSQL(array $columnDef) + { + $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision'])) + ? 10 : $columnDef['precision']; + $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale'])) + ? 0 : $columnDef['scale']; + + return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')'; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field The field definition array. + * + * @return string DBMS specific SQL code portion needed to set a default value. + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! isset($field['default'])) { + return empty($field['notnull']) ? ' DEFAULT NULL' : ''; + } + + $default = $field['default']; + + if ( ! isset($field['type'])) { + return " DEFAULT '" . $default . "'"; + } + + $type = (string) $field['type']; + + if (in_array($type, ["Integer", "BigInt", "SmallInt"], true)) { + return " DEFAULT " . $default; + } + + if (in_array($type, ['DateTime', 'DateTimeTz', 'DateTimeImmutable', 'DateTimeTzImmutable'], true) && $default === $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if (in_array($type, ['Time', 'TimeImmutable'], true) && $default === $this->getCurrentTimeSQL()) { + return " DEFAULT " . $this->getCurrentTimeSQL(); + } + + if (in_array($type, ['Date', 'DateImmutable'], true) && $default === $this->getCurrentDateSQL()) { + return " DEFAULT " . $this->getCurrentDateSQL(); + } + + if ($type === 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($default) . "'"; + } + + return " DEFAULT '" . $default . "'"; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition The check definition. + * + * @return string DBMS specific SQL code portion needed to set a CHECK constraint. + */ + public function getCheckDeclarationSQL(array $definition) + { + $constraints = array(); + foreach ($definition as $field => $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; + } + + if (isset($def['max'])) { + $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; + } + } + } + + return implode(', ', $constraints); + } + + /** + * Obtains DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the unique constraint. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set a constraint. + * + * @throws \InvalidArgumentException + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the index. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set an index. + * + * @throws \InvalidArgumentException + */ + public function getIndexDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains SQL code portion needed to create a custom column, + * e.g. when a field has the "columnDefinition" keyword. + * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. + * + * @param array $columnDef + * + * @return string + */ + public function getCustomTypeDeclarationSQL(array $columnDef) + { + return $columnDef['columnDefinition']; + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param array $fields + * + * @return string + */ + public function getIndexFieldDeclarationListSQL(array $fields) + { + $ret = array(); + + foreach ($fields as $field => $definition) { + if (is_array($definition)) { + $ret[] = $field; + } else { + $ret[] = $definition; + } + } + + return implode(', ', $ret); + } + + /** + * Returns the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * Should be overridden in driver classes to return the correct string for the + * specific database type. + * + * The default is to return the string "TEMPORARY" - this will result in a + * SQL error for any database that does not support temporary tables, or that + * requires a different SQL command from "CREATE TEMPORARY TABLE". + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableSQL() + { + return 'TEMPORARY'; + } + + /** + * Some vendors require temporary table names to be qualified specially. + * + * @param string $tableName + * + * @return string + */ + public function getTemporaryTableName($tableName) + { + return $tableName; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration. + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); + $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $sql; + } + + /** + * Returns the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key definition. + * + * @return string + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); + } + if ($foreignKey->hasOption('onDelete')) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $query; + } + + /** + * Returns the given referential action in uppercase if valid, otherwise throws an exception. + * + * @param string $action The foreign key referential action. + * + * @return string + * + * @throws \InvalidArgumentException if unknown referential action given + */ + public function getForeignKeyReferentialActionSQL($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + default: + throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); + } + } + + /** + * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + if (strlen($foreignKey->getName())) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + $sql .= 'FOREIGN KEY ('; + + if (count($foreignKey->getLocalColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + if (count($foreignKey->getForeignColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + if (strlen($foreignKey->getForeignTableName()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + $sql .= implode(', ', $foreignKey->getQuotedLocalColumns($this)) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; + + return $sql; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration. + */ + public function getUniqueFieldDeclarationSQL() + { + return 'UNIQUE'; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $charset The name of the charset. + * + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration. + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return ''; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation The name of the collation. + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getColumnCollationDeclarationSQL($collation) + { + return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; + } + + /** + * Whether the platform prefers sequences for ID generation. + * Subclasses should override this method to return TRUE if they prefer sequences. + * + * @return boolean + */ + public function prefersSequences() + { + return false; + } + + /** + * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. + * Subclasses should override this method to return TRUE if they prefer identity columns. + * + * @return boolean + */ + public function prefersIdentityColumns() + { + return false; + } + + /** + * Some platforms need the boolean values to be converted. + * + * The default conversion in this implementation converts to integers (false => 0, true => 1). + * + * Note: if the input is not a boolean the original input might be returned. + * + * There are two contexts when converting booleans: Literals and Prepared Statements. + * This method should handle the literal case + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } elseif (is_bool($item)) { + $item = (int) $item; + } + + return $item; + } + + /** + * Some platforms have boolean literals that needs to be correctly converted + * + * The default conversion tries to convert value into bool "(bool)$item" + * + * @param mixed $item + * + * @return bool|null + */ + public function convertFromBoolean($item) + { + return null === $item ? null: (bool) $item ; + } + + /** + * This method should handle the prepared statements case. When there is no + * distinction, it's OK to use the same method. + * + * Note: if the input is not a boolean the original input might be returned. + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleansToDatabaseValue($item) + { + return $this->convertBooleans($item); + } + + /** + * Returns the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Returns the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Returns the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Returns the SQL for a given transaction isolation level Connection constant. + * + * @param integer $level + * + * @return string + * + * @throws \InvalidArgumentException + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case Connection::TRANSACTION_REPEATABLE_READ: + return 'REPEATABLE READ'; + case Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL statement for retrieving the namespaces defined in the database. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListNamespacesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to list all views of a database or user. + * + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the list of indexes for the current database. + * + * The current database parameter is optional but will always be passed + * when using the SchemaManager API and is the database the given table is in. + * + * Attention: Some platforms only support currentDatabase when they + * are connected with that database. Cross-database information schema + * requests may be impossible. + * + * @param string $table + * @param string $currentDatabase + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * @param string $sql + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to drop an existing sequence. + * + * @param Sequence|string $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $sequenceName + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a new database. + * + * @param string $database The name of the database that should be created. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to set the transaction isolation level. + * + * @param integer $level + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + + /** + * Obtains DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param array $fieldDeclaration + * + * @return string + */ + public function getFloatDeclarationSQL(array $fieldDeclaration) + { + return 'DOUBLE PRECISION'; + } + + /** + * Gets the default transaction isolation level of the platform. + * + * @return integer The default isolation level. + * + * @see Doctrine\DBAL\Connection\TRANSACTION_* constants. + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_COMMITTED; + } + + /* supports*() methods */ + + /** + * Whether the platform supports sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return false; + } + + /** + * Whether the platform supports identity columns. + * + * Identity columns are columns that receive an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform emulates identity columns through sequences. + * + * Some platforms that do not support identity columns natively + * but support sequences can emulate identity columns by using + * sequences. + * + * @return boolean + */ + public function usesSequenceEmulatedIdentityColumns() + { + return false; + } + + /** + * Returns the name of the sequence for a particular identity column in a particular table. + * + * @param string $tableName The name of the table to return the sequence name for. + * @param string $columnName The name of the identity column in the table to return the sequence name for. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + * + * @see usesSequenceEmulatedIdentityColumns + */ + public function getIdentitySequenceName($tableName, $columnName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + /** + * Whether the platform supports partial indexes. + * + * @return boolean + */ + public function supportsPartialIndexes() + { + return false; + } + + /** + * Whether the platform supports altering tables. + * + * @return boolean + */ + public function supportsAlterTable() + { + return true; + } + + /** + * Whether the platform supports transactions. + * + * @return boolean + */ + public function supportsTransactions() + { + return true; + } + + /** + * Whether the platform supports savepoints. + * + * @return boolean + */ + public function supportsSavepoints() + { + return true; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return $this->supportsSavepoints(); + } + + /** + * Whether the platform supports primary key constraints. + * + * @return boolean + */ + public function supportsPrimaryConstraints() + { + return true; + } + + /** + * Whether the platform supports foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Whether this platform supports onUpdate in foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyOnUpdate() + { + return ($this->supportsForeignKeyConstraints() && true); + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } + + /** + * Whether this platform can emulate schemas. + * + * Platforms that either support or emulate schemas don't automatically + * filter a schema for the namespaced elements in {@link + * AbstractManager#createSchema}. + * + * @return boolean + */ + public function canEmulateSchemas() + { + return false; + } + + /** + * Returns the default schema name. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDefaultSchemaName() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether this platform supports create database. + * + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return boolean + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * Whether the platform supports getting the affected rows of a recent update/delete type query. + * + * @return boolean + */ + public function supportsGettingAffectedRows() + { + return true; + } + + /** + * Whether this platform support to add inline column comments as postfix. + * + * @return boolean + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Whether this platform support the proprietary syntax "COMMENT ON asset". + * + * @return boolean + */ + public function supportsCommentOnStatement() + { + return false; + } + + /** + * Does this platform have native guid type. + * + * @return boolean + */ + public function hasNativeGuidType() + { + return false; + } + + /** + * Does this platform have native JSON type. + * + * @return boolean + */ + public function hasNativeJsonType() + { + return false; + } + + /** + * @deprecated + * @todo Remove in 3.0 + */ + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Whether this platform supports views. + * + * @return boolean + */ + public function supportsViews() + { + return true; + } + + /** + * Does this platform support column collation? + * + * @return boolean + */ + public function supportsColumnCollation() + { + return false; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime value of this platform. + * + * @return string The format string. + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime with timezone value of this platform. + * + * @return string The format string. + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored date value of this platform. + * + * @return string The format string. + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored time value of this platform. + * + * @return string The format string. + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + * + * @throws DBALException + */ + final public function modifyLimitQuery($query, $limit, $offset = null) + { + if ($limit !== null) { + $limit = (int) $limit; + } + + if ($offset !== null) { + $offset = (int) $offset; + + if ($offset < 0) { + throw new DBALException("LIMIT argument offset=$offset is not valid"); + } + if ($offset > 0 && ! $this->supportsLimitOffset()) { + throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName())); + } + } + + return $this->doModifyLimitQuery($query, $limit, $offset); + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Whether the database platform support offsets in modify limit clauses. + * + * @return boolean + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * @param string $column The column name for which to get the correct character casing. + * + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return $column; + } + + /** + * Makes any fixes to a name of a schema element (table, sequence, ...) that are required + * by restrictions of the platform, like a maximum length. + * + * @param string $schemaElementName + * + * @return string + */ + public function fixSchemaElementName($schemaElementName) + { + return $schemaElementName; + } + + /** + * Maximum length of any given database identifier, like tables or column names. + * + * @return integer + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Returns the insert SQL for an empty insert statement. + * + * @param string $tableName + * @param string $identifierColumnName + * + * @return string + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generates a Truncate Table SQL statement for a given table. + * + * Cascade is not supported on many platforms but would optionally cascade the truncate by + * following the foreign keys. + * + * @param string $tableName + * @param boolean $cascade + * + * @return string + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * This is for test reasons, many vendors have special requirements for dummy statements. + * + * @return string + */ + public function getDummySelectSQL() + { + return 'SELECT 1'; + } + + /** + * Returns the SQL to create a new savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to release a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to rollback a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Returns the keyword list instance of this platform. + * + * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList + * + * @throws \Doctrine\DBAL\DBALException If no keyword list is specified. + */ + final public function getReservedKeywordsList() + { + // Check for an existing instantiation of the keywords class. + if ($this->_keywords) { + return $this->_keywords; + } + + $class = $this->getReservedKeywordsClass(); + $keywords = new $class; + if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { + throw DBALException::notSupported(__METHOD__); + } + + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $keywords; + + return $keywords; + } + + /** + * Returns the class name of the reserved keywords list. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Quotes a literal string. + * This method is NOT meant to fix SQL injections! + * It is only meant to escape this platform's string literal + * quote character inside the given literal string. + * + * @param string $str The literal string to be quoted. + * + * @return string The quoted literal string. + */ + public function quoteStringLiteral($str) + { + $c = $this->getStringLiteralQuoteCharacter(); + + return $c . str_replace($c, $c . $c, $str) . $c; + } + + /** + * Gets the character used for string literal quoting. + * + * @return string + */ + public function getStringLiteralQuoteCharacter() + { + return "'"; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/DB2Platform.php b/wbce/include/Doctrine/DBAL/Platforms/DB2Platform.php new file mode 100644 index 000000000..cac98333e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,881 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; + +class DB2Platform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32704; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + // todo blob(n) with $field['length']; + return 'BLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'varbinary' => 'binary', + 'binary' => 'binary', + 'clob' => 'text', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'double' => 'float', + 'real' => 'float', + 'timestamp' => 'datetime', + ); + } + + /** + * {@inheritdoc} + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($doctrineType->getName() === Type::BOOLEAN) { + // We require a commented boolean type in order to distinguish between boolean and smallint + // as both (have to) map to the same native type. + return true; + } + + return parent::isCommentedDoctrineType($doctrineType); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'db2'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + + return $autoinc; + } + + /** + * {@inheritdoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return 'BITOR(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE'; + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. + * + * @license New BSD License + * + * @param string $table + * @param string $database + * + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->quoteStringLiteral($table); + + // We do the funky subquery and join syscat.columns.default this crazy way because + // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, + // it wants shorter stuff like a varchar. + return " + SELECT + cols.default, + subq.* + FROM ( + SELECT DISTINCT + c.tabschema, + c.tabname, + c.colname, + c.colno, + c.typename, + c.nulls, + c.length, + c.scale, + c.identity, + tc.type AS tabconsttype, + c.remarks AS comment, + k.colseq, + CASE + WHEN c.generated = 'D' THEN 1 + ELSE 0 + END AS autoincrement + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE UPPER(c.tabname) = UPPER(" . $table . ") + ORDER BY c.colno + ) subq + JOIN syscat.columns cols + ON subq.tabschema = cols.tabschema + AND subq.tabname = cols.tabname + AND subq.colno = cols.colno + ORDER BY subq.colno + "; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = $this->quoteStringLiteral($table); + + return "SELECT idx.INDNAME AS key_name, + idxcol.COLNAME AS column_name, + CASE + WHEN idx.UNIQUERULE = 'P' THEN 1 + ELSE 0 + END AS primary, + CASE + WHEN idx.UNIQUERULE = 'D' THEN 1 + ELSE 0 + END AS non_unique + FROM SYSCAT.INDEXES AS idx + JOIN SYSCAT.INDEXCOLUSE AS idxcol + ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME + WHERE idx.TABNAME = UPPER(" . $table . ") + ORDER BY idxcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + $table = $this->quoteStringLiteral($table); + + return "SELECT fkcol.COLNAME AS local_column, + fk.REFTABNAME AS foreign_table, + pkcol.COLNAME AS foreign_column, + fk.CONSTNAME AS index_name, + CASE + WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE + WHEN fk.DELETERULE = 'C' THEN 'CASCADE' + WHEN fk.DELETERULE = 'N' THEN 'SET NULL' + WHEN fk.DELETERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete + FROM SYSCAT.REFERENCES AS fk + JOIN SYSCAT.KEYCOLUSE AS fkcol + ON fk.CONSTNAME = fkcol.CONSTNAME + AND fk.TABSCHEMA = fkcol.TABSCHEMA + AND fk.TABNAME = fkcol.TABNAME + JOIN SYSCAT.KEYCOLUSE AS pkcol + ON fk.REFKEYNAME = pkcol.CONSTNAME + AND fk.REFTABSCHEMA = pkcol.TABSCHEMA + AND fk.REFTABNAME = pkcol.TABNAME + WHERE fk.TABNAME = UPPER(" . $table . ") + ORDER BY fkcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE " . $database; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimestampSQL() + { + return "CURRENT TIMESTAMP"; + } + + /** + * {@inheritDoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $indexes = array(); + if (isset($options['indexes'])) { + $indexes = $options['indexes']; + } + $options['indexes'] = array(); + + $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); + + foreach ($indexes as $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + + $queryParts = array(); + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + // Adding non-nullable columns to a table requires a default value to be specified. + if ( ! empty($columnDef['notnull']) && + ! isset($columnDef['default']) && + empty($columnDef['autoincrement']) + ) { + $queryPart .= ' WITH DEFAULT'; + } + + $queryParts[] = $queryPart; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $columnDiff->column->getQuotedName($this), + $this->getColumnComment($columnDiff->column) + ); + + if (count($columnDiff->changedProperties) === 1) { + continue; + } + } + + $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . + ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts); + } + + // Some table alteration operations require a table reorganization. + if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) { + $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')"; + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Gathers the table alteration SQL for a given column diff. + * + * @param Table $table The table to gather the SQL for. + * @param ColumnDiff $columnDiff The column diff to evaluate. + * @param array $sql The sequence of table alteration statements to fill. + * @param array $queryParts The sequence of column alteration clauses to fill. + */ + private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) + { + $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); + + if (empty($alterColumnClauses)) { + return; + } + + // If we have a single column alteration, we can append the clause to the main query. + if (count($alterColumnClauses) === 1) { + $queryParts[] = current($alterColumnClauses); + + return; + } + + // We have multiple alterations for the same column, + // so we need to trigger a complete ALTER TABLE statement + // for each ALTER COLUMN clause. + foreach ($alterColumnClauses as $alterColumnClause) { + $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause; + } + } + + /** + * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return array + */ + private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) + { + $column = $columnDiff->column->toArray(); + + $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); + + if ($column['columnDefinition']) { + return array($alterClause . ' ' . $column['columnDefinition']); + } + + $clauses = array(); + + if ($columnDiff->hasChanged('type') || + $columnDiff->hasChanged('length') || + $columnDiff->hasChanged('precision') || + $columnDiff->hasChanged('scale') || + $columnDiff->hasChanged('fixed') + ) { + $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this); + } + + if ($columnDiff->hasChanged('notnull')) { + $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; + } + + if ($columnDiff->hasChanged('default')) { + if (isset($column['default'])) { + $defaultClause = $this->getDefaultValueDeclarationSQL($column); + + if ($defaultClause) { + $clauses[] = $alterClause . ' SET' . $defaultClause; + } + } else { + $clauses[] = $alterClause . ' DROP DEFAULT'; + } + } + + return $clauses; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + if ($remIndex->isPrimary()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } elseif ($remIndex->isUnique()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this); + } else { + $sql[] = $this->getDropIndexSQL($remIndex, $table); + } + + $sql[] = $this->getCreateIndexSQL($addIndex, $table); + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! empty($field['autoincrement'])) { + return ''; + } + + if (isset($field['version']) && $field['version']) { + if ((string) $field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + $where = array(); + + if ($offset > 0) { + $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1); + } + + if ($limit !== null) { + $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit); + } + + if (empty($where)) { + return $query; + } + + // Todo OVER() needs ORDER BY data! + return sprintf( + 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s', + $query, + implode(' AND ', $where) + ); + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * DB2 returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' WITH RR USE AND KEEP UPDATE LOCKS'; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM sysibm.sysdummy1'; + } + + /** + * {@inheritDoc} + * + * DB2 supports savepoints, but they work semantically different than on other vendor platforms. + * + * TODO: We have to investigate how to get DB2 up and running with savepoints. + */ + public function supportsSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\DB2Keywords::class; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/wbce/include/Doctrine/DBAL/Platforms/DrizzlePlatform.php new file mode 100644 index 000000000..851e5aad9 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -0,0 +1,624 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BinaryType; + +/** + * Drizzle platform + * + * @author Kim Hemsø Rasmussen + */ +class DrizzlePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'varchar' => 'string', + 'varbinary' => 'binary', + 'integer' => 'integer', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'datetime' => 'datetime', + 'date' => 'date', + 'time' => 'time', + 'text' => 'text', + 'timestamp' => 'datetime', + 'double' => 'float', + 'bigint' => 'bigint', + ); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\DrizzleKeywords::class; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT, CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT," . + " NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME" . + " FROM DATA_DICTIONARY.COLUMNS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS, UPDATE_RULE, DELETE_RULE" . + " FROM DATA_DICTIONARY.FOREIGN_KEYS" . + " WHERE CONSTRAINT_SCHEMA=" . $database . " AND CONSTRAINT_TABLE='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT INDEX_NAME AS 'key_name', COLUMN_NAME AS 'column_name', IS_USED_IN_PRIMARY AS 'primary', IS_UNIQUE=0 AS 'non_unique'" . + " FROM DATA_DICTIONARY.INDEX_PARTS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsViews() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // drizzle primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * {@inheritDoc} + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Drizzle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($columnArray['type'] instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 'true' : 'false'; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php new file mode 100644 index 000000000..ff8aac81c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,441 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'DB2'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ACTIVATE', + 'ADD', + 'AFTER', + 'ALIAS', + 'ALL', + 'ALLOCATE', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', + 'ALLOW', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASENSITIVE', + 'ASSOCIATE', + 'ASUTIME', + 'AT', + 'ATTRIBUTES', + 'AUDIT', + 'AUTHORIZATION', + 'AUX', + 'AUXILIARY', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BINARY', + 'BUFFERPOOL', + 'BY', + 'CACHE', + 'CALL', + 'CALLED', + 'CAPTURE', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CCSID', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'CLONE', + 'CLOSE', + 'CLUSTER', + 'COLLECTION', + 'COLLID', + 'COLUMN', + 'COMMENT', + 'COMMIT', + 'CONCAT', + 'CONDITION', + 'CONNECT', + 'CONNECTION', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'COUNT', + 'COUNT_BIG', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_LC_CTYPE', + 'CURRENT_PATH', + 'CURRENT_SCHEMA', + 'CURRENT_SERVER', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TIMEZONE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATA', + 'DATABASE', + 'DATAPARTITIONNAME', + 'DATAPARTITIONNUM', + 'EDITPROC', + 'ELSE', + 'ELSEIF', + 'ENABLE', + 'ENCODING', + 'ENCRYPTION', + 'END', + 'END-EXEC', + 'ENDING', + 'ERASE', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXCEPTION', + 'EXCLUDING', + 'EXCLUSIVE', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'EXTERNAL', + 'EXTRACT', + 'FENCED', + 'FETCH', + 'FIELDPROC', + 'FILE', + 'FINAL', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GENERAL', + 'GENERATED', + 'GET', + 'GLOBAL', + 'GO', + 'GOTO', + 'GRANT', + 'GRAPHIC', + 'GROUP', + 'HANDLER', + 'HASH', + 'HASHED_VALUE', + 'HAVING', + 'HINT', + 'HOLD', + 'HOUR', + 'HOURS', + 'IDENTITY', + 'IF', + 'IMMEDIATE', + 'IN', + 'INCLUDING', + 'INCLUSIVE', + 'INCREMENT', + 'INDEX', + 'INDICATOR', + 'INF', + 'INFINITY', + 'INHERIT', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INTEGRITY', + 'MATERIALIZED', + 'MAXVALUE', + 'MICROSECOND', + 'MICROSECONDS', + 'MINUTE', + 'MINUTES', + 'MINVALUE', + 'MODE', + 'MODIFIES', + 'MONTH', + 'MONTHS', + 'NAN', + 'NEW', + 'NEW_TABLE', + 'NEXTVAL', + 'NO', + 'NOCACHE', + 'NOCYCLE', + 'NODENAME', + 'NODENUMBER', + 'NOMAXVALUE', + 'NOMINVALUE', + 'NONE', + 'NOORDER', + 'NORMALIZED', + 'NOT', + 'NULL', + 'NULLS', + 'NUMPARTS', + 'OBID', + 'OF', + 'OLD', + 'OLD_TABLE', + 'ON', + 'OPEN', + 'OPTIMIZATION', + 'OPTIMIZE', + 'OPTION', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERRIDING', + 'PACKAGE', + 'PADDED', + 'PAGESIZE', + 'PARAMETER', + 'PART', + 'PARTITION', + 'PARTITIONED', + 'PARTITIONING', + 'PARTITIONS', + 'PASSWORD', + 'PATH', + 'PIECESIZE', + 'PLAN', + 'POSITION', + 'PRECISION', + 'PREPARE', + 'PREVVAL', + 'PRIMARY', + 'PRIQTY', + 'PRIVILEGES', + 'PROCEDURE', + 'PROGRAM', + 'PSID', + 'ROUND_UP', + 'ROUTINE', + 'ROW', + 'ROW_NUMBER', + 'ROWNUMBER', + 'ROWS', + 'ROWSET', + 'RRN', + 'RUN', + 'SAVEPOINT', + 'SCHEMA', + 'SCRATCHPAD', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SECONDS', + 'SECQTY', + 'SECURITY', + 'SELECT', + 'SENSITIVE', + 'SEQUENCE', + 'SESSION', + 'SESSION_USER', + 'SET', + 'SIGNAL', + 'SIMPLE', + 'SNAN', + 'SOME', + 'SOURCE', + 'SPECIFIC', + 'SQL', + 'SQLID', + 'STACKED', + 'STANDARD', + 'START', + 'STARTING', + 'STATEMENT', + 'STATIC', + 'STATMENT', + 'STAY', + 'STOGROUP', + 'STORES', + 'STYLE', + 'SUBSTRING', + 'SUMMARY', + 'SYNONYM', + 'SYSFUN', + 'SYSIBM', + 'SYSPROC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESPACE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'TRIM', + 'TRUNCATE', + 'TYPE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNTIL', + 'UPDATE', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'INTERSECT', + 'PUBLIC', + 'USAGE', + 'INTO', + 'QUERY', + 'USER', + 'IS', + 'QUERYNO', + 'USING', + 'ISOBID', + 'RANGE', + 'VALIDPROC', + 'ISOLATION', + 'RANK', + 'VALUE', + 'ITERATE', + 'READ', + 'VALUES', + 'JAR', + 'READS', + 'VARIABLE', + 'JAVA', + 'RECOVERY', + 'VARIANT', + 'JOIN', + 'REFERENCES', + 'VCAT', + 'KEEP', + 'REFERENCING', + 'VERSION', + 'KEY', + 'REFRESH', + 'VIEW', + 'LABEL', + 'RELEASE', + 'VOLATILE', + 'LANGUAGE', + 'RENAME', + 'VOLUMES', + 'LATERAL', + 'REPEAT', + 'WHEN', + 'LC_CTYPE', + 'RESET', + 'WHENEVER', + 'LEAVE', + 'RESIGNAL', + 'WHERE', + 'LEFT', + 'RESTART', + 'WHILE', + 'LIKE', + 'RESTRICT', + 'WITH', + 'LINKTYPE', + 'RESULT', + 'WITHOUT', + 'LOCAL', + 'RESULT_SET_LOCATOR WLM', + 'LOCALDATE', + 'RETURN', + 'WRITE', + 'LOCALE', + 'RETURNS', + 'XMLELEMENT', + 'LOCALTIME', + 'REVOKE', + 'XMLEXISTS', + 'LOCALTIMESTAMP RIGHT', + 'XMLNAMESPACES', + 'LOCATOR', + 'ROLE', + 'YEAR', + 'LOCATORS', + 'ROLLBACK', + 'YEARS', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php new file mode 100644 index 000000000..21d59d8c5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php @@ -0,0 +1,345 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Drizzle Keywordlist. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ABS', + 'ALL', + 'ALLOCATE', + 'ALTER', + 'AND', + 'ANY', + 'ARE', + 'ARRAY', + 'AS', + 'ASENSITIVE', + 'ASYMMETRIC', + 'AT', + 'ATOMIC', + 'AUTHORIZATION', + 'AVG', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', + 'BOTH', + 'BY', + 'CALL', + 'CALLED', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CEIL', + 'CEILING', + 'CHAR', + 'CHARACTER', + 'CHARACTER_LENGTH', + 'CHAR_LENGTH', + 'CHECK', + 'CLOB', + 'CLOSE', + 'COALESCE', + 'COLLATE', + 'COLLECT', + 'COLUMN', + 'COMMIT', + 'CONDITION', + 'CONNECT', + 'CONSTRAINT', + 'CONVERT', + 'CORR', + 'CORRESPONDING', + 'COUNT', + 'COVAR_POP', + 'COVAR_SAMP', + 'CREATE', + 'CROSS', + 'CUBE', + 'CUME_DIST', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_DEFAULT_TRANSFORM_GROUP', + 'CURRENT_PATH', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TRANSFORM_GROUP_FOR_TYPE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATE', + 'DAY', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENSE_RANK', + 'DEREF', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISCONNECT', + 'DISTINCT', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'EACH', + 'ELEMENT', + 'ELSE', + 'END', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXP', + 'EXTERNAL', + 'EXTRACT', + 'FALSE', + 'FETCH', + 'FILTER', + 'FLOAT', + 'FLOOR', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'FUSION', + 'GET', + 'GLOBAL', + 'GRANT', + 'GROUP', + 'GROUPING', + 'HAVING', + 'HOLD', + 'HOUR', + 'IDENTITY', + 'IN', + 'INDICATOR', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INTEGER', + 'INTERSECT', + 'INTERSECTION', + 'INTERVAL', + 'INTO', + 'IS', + 'JOIN', + 'LANGUAGE', + 'LARGE', + 'LATERAL', + 'LEADING', + 'LEFT', + 'LIKE', + 'LN', + 'LOCAL', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOWER', + 'MATCH', + 'MAX', + 'MEMBER', + 'MERGE', + 'METHOD', + 'MIN', + 'MINUTE', + 'MOD', + 'MODIFIES', + 'MODULE', + 'MONTH', + 'MULTISET', + 'NATIONAL', + 'NATURAL', + 'NCHAR', + 'NCLOB', + 'NEW', + 'NO', + 'NONE', + 'NORMALIZE', + 'NOT', + 'NULL_SYM', + 'NULLIF', + 'NUMERIC', + 'OCTET_LENGTH', + 'OF', + 'OLD', + 'ON', + 'ONLY', + 'OPEN', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'OVERLAY', + 'PARAMETER', + 'PARTITION', + 'PERCENTILE_CONT', + 'PERCENTILE_DISC', + 'PERCENT_RANK', + 'POSITION', + 'POWER', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PROCEDURE', + 'RANGE', + 'RANK', + 'READS', + 'REAL', + 'RECURSIVE', + 'REF', + 'REFERENCES', + 'REFERENCING', + 'REGR_AVGX', + 'REGR_AVGY', + 'REGR_COUNT', + 'REGR_INTERCEPT', + 'REGR_R2', + 'REGR_SLOPE', + 'REGR_SXX', + 'REGR_SXY', + 'REGR_SYY', + 'RELEASE', + 'RESULT', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'ROW', + 'ROWS', + 'ROW_NUMBER', + 'SAVEPOINT', + 'SCOPE', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SELECT', + 'SENSITIVE', + 'SESSION_USER', + 'SET', + 'SIMILAR', + 'SMALLINT', + 'SOME', + 'SPECIFIC', + 'SPECIFICTYPE', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQRT', + 'START', + 'STATIC', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'SUBMULTISET', + 'SUBSTRING', + 'SUM', + 'SYMMETRIC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TIMEZONE_HOUR', + 'TIMEZONE_MINUTE', + 'TO', + 'TRAILING', + 'TRANSLATE', + 'TRANSLATION', + 'TREAT', + 'TRIGGER', + 'TRIM', + 'TRUE', + 'UESCAPE', + 'UNION', + 'UNIQUE', + 'UNKNOWN', + 'UNNEST', + 'UPDATE', + 'UPPER', + 'USER', + 'USING', + 'VALUE', + 'VALUES', + 'VARCHAR', + 'VARYING', + 'VAR_POP', + 'VAR_SAMP', + 'WHEN', + 'WHENEVER', + 'WHERE', + 'WIDTH_BUCKET', + 'WINDOW', + 'WITH', + 'WITHIN', + 'WITHOUT', + 'XML', + 'XMLAGG', + 'XMLATTRIBUTES', + 'XMLBINARY', + 'XMLCOMMENT', + 'XMLCONCAT', + 'XMLELEMENT', + 'XMLFOREST', + 'XMLNAMESPACES', + 'XMLPARSE', + 'XMLPI', + 'XMLROOT', + 'XMLSERIALIZE', + 'YEAR', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/KeywordList.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/KeywordList.php new file mode 100644 index 000000000..5b8d74d01 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + /** + * @var array|null + */ + private $keywords = null; + + /** + * Checks if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * + * @return boolean + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + /** + * @return void + */ + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + /** + * Returns the list of keywords. + * + * @return array + */ + abstract protected function getKeywords(); + + /** + * Returns the name of this keyword list. + * + * @return string + */ + abstract public function getName(); +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php new file mode 100644 index 000000000..9c8f61499 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,43 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MsSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + * @deprecated Use SQLServerKeywords class instead. + */ +class MsSQLKeywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MsSQL'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php new file mode 100644 index 000000000..ae2f5bc73 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php @@ -0,0 +1,284 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL 5.7 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Keywords extends MySQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL57'; + } + + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-7.html + */ + protected function getKeywords() + { + return array( + 'ACCESSIBLE', + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GENERATED', + 'GET', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IO_AFTER_GTIDS', + 'IO_BEFORE_GTIDS', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINEAR', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MASTER_BIND', + 'MASTER_SSL_VERIFY_SERVER_CERT', + 'MATCH', + 'MAXVALUE', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NO_WRITE_TO_BINLOG', + 'NOT', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTIMIZER_COSTS', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PARTITION', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RANGE', + 'READ', + 'READ_WRITE', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESIGNAL', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SIGNAL', + 'SMALLINT', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SSL', + 'STARTING', + 'STORED', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'VIRTUAL', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php new file mode 100644 index 000000000..fad3aa153 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,287 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MySQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ACCESSIBLE', + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONNECTION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GENERAL', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IGNORE_SERVER_IDS', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LABEL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINEAR', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MASTER_HEARTBEAT_PERIOD', + 'MASTER_SSL_VERIFY_SERVER_CERT', + 'MATCH', + 'MAXVALUE', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NO_WRITE_TO_BINLOG', + 'NOT', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PARTITION', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RAID0', + 'RANGE', + 'READ', + 'READ_WRITE', + 'READS', + 'REAL', + 'RECURSIVE', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESIGNAL', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'ROWS', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SIGNAL', + 'SLOW', + 'SMALLINT', + 'SONAME', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'X509', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php new file mode 100644 index 000000000..b37fdad3d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class OracleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'Oracle'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ACCESS', + 'ELSE', + 'MODIFY', + 'START', + 'ADD', + 'EXCLUSIVE', + 'NOAUDIT', + 'SELECT', + 'ALL', + 'EXISTS', + 'NOCOMPRESS', + 'SESSION', + 'ALTER', + 'FILE', + 'NOT', + 'SET', + 'AND', + 'FLOAT', + 'NOTFOUND ', + 'SHARE', + 'ANY', + 'FOR', + 'NOWAIT', + 'SIZE', + 'ARRAYLEN', + 'FROM', + 'NULL', + 'SMALLINT', + 'AS', + 'GRANT', + 'NUMBER', + 'SQLBUF', + 'ASC', + 'GROUP', + 'OF', + 'SUCCESSFUL', + 'AUDIT', + 'HAVING', + 'OFFLINE ', + 'SYNONYM', + 'BETWEEN', + 'IDENTIFIED', + 'ON', + 'SYSDATE', + 'BY', + 'IMMEDIATE', + 'ONLINE', + 'TABLE', + 'CHAR', + 'IN', + 'OPTION', + 'THEN', + 'CHECK', + 'INCREMENT', + 'OR', + 'TO', + 'CLUSTER', + 'INDEX', + 'ORDER', + 'TRIGGER', + 'COLUMN', + 'INITIAL', + 'PCTFREE', + 'UID', + 'COMMENT', + 'INSERT', + 'PRIOR', + 'UNION', + 'COMPRESS', + 'INTEGER', + 'PRIVILEGES', + 'UNIQUE', + 'CONNECT', + 'INTERSECT', + 'PUBLIC', + 'UPDATE', + 'CREATE', + 'INTO', + 'RAW', + 'USER', + 'CURRENT', + 'IS', + 'RENAME', + 'VALIDATE', + 'DATE', + 'LEVEL', + 'RESOURCE', + 'VALUES', + 'DECIMAL', + 'LIKE', + 'REVOKE', + 'VARCHAR', + 'DEFAULT', + 'LOCK', + 'ROW', + 'VARCHAR2', + 'DELETE', + 'LONG', + 'ROWID', + 'VIEW', + 'DESC', + 'MAXEXTENTS', + 'ROWLABEL', + 'WHENEVER', + 'DISTINCT', + 'MINUS', + 'ROWNUM', + 'WHERE', + 'DROP', + 'MODE', + 'ROWS', + 'WITH', + 'RANGE', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php new file mode 100644 index 000000000..4e5c632d3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.1 reserved keywords list. + * + * @author Martin Hasoň + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Keywords extends PostgreSQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL91'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.1/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'ARRAY', + 'AS', + 'ASC', + 'ASYMMETRIC', + 'AUTHORIZATION', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONCURRENTLY', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_CATALOG', + 'CURRENT_DATE', + 'CURRENT_ROLE', + 'CURRENT_SCHEMA', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FETCH', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFFSET', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'RETURNING', + 'RIGHT', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'SYMMETRIC', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VARIADIC', + 'VERBOSE', + 'WHEN', + 'WHERE', + 'WINDOW', + 'WITH', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php new file mode 100644 index 000000000..7180dfd98 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.2 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Keywords extends PostgreSQL91Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL92'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.2/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'COLLATION', + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL94Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL94Keywords.php new file mode 100644 index 000000000..dfb2a0633 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQL94Keywords.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.4 reserved keywords list. + * + * @author Matteo Beccati + * @link www.doctrine-project.org + * @since 2.6 + */ +class PostgreSQL94Keywords extends PostgreSQL92Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL94'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.4/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + $parentKeywords = array_diff(parent::getKeywords(), array( + 'OVER', + )); + + return array_merge($parentKeywords, array( + 'LATERAL', + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 000000000..9e49d7f25 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php new file mode 100644 index 000000000..e1fdd492d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,143 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class ReservedKeywordsValidator implements Visitor +{ + /** + * @var KeywordList[] + */ + private $keywordLists = array(); + + /** + * @var array + */ + private $violations = array(); + + /** + * @param \Doctrine\DBAL\Platforms\Keywords\KeywordList[] $keywordLists + */ + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + /** + * @return array + */ + public function getViolations() + { + return $this->violations; + } + + /** + * @param string $word + * + * @return array + */ + private function isReservedWord($word) + { + if ($word[0] == "`") { + $word = str_replace('`', '', $word); + } + + $keywordLists = array(); + foreach ($this->keywordLists as $keywordList) { + if ($keywordList->isKeyword($word)) { + $keywordLists[] = $keywordList->getName(); + } + } + + return $keywordLists; + } + + /** + * @param string $asset + * @param array $violatedPlatforms + * + * @return void + */ + private function addViolation($asset, $violatedPlatforms) + { + if ( ! $violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->addViolation( + 'Table ' . $table->getName(), + $this->isReservedWord($table->getName()) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php new file mode 100644 index 000000000..12eacbf9b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 11 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere11Keywords extends SQLAnywhereKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere11'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1100/en/dbreference_en11/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array('IQ') + ), + array( + 'MERGE', + 'OPENSTRING' + ) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php new file mode 100644 index 000000000..22e04893e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 12 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere12Keywords extends SQLAnywhere11Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere12'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1200/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array( + 'INDEX_LPAREN', + 'SYNTAX_ERROR', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP' + ) + ), + array( + 'DATETIMEOFFSET', + 'LIMIT', + 'OPENXML', + 'SPATIAL', + 'TREAT' + ) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php new file mode 100644 index 000000000..4d78b8410 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 16 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere16Keywords extends SQLAnywhere12Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere16'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + parent::getKeywords(), + array( + 'ARRAY', + 'JSON', + 'ROW', + 'ROWTYPE', + 'UNNEST', + 'VARRAY' + ) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php new file mode 100644 index 000000000..cabf1e449 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php @@ -0,0 +1,281 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 10 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhereKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere'; + } + + /** + * {@inheritdoc} + * + * @link http://infocenter.sybase.com/help/topic/com.sybase.dbrfen10/pdf/dbrfen10.pdf?noframes=true + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'ATTACH', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BIT', + 'BOTTOM', + 'BREAK', + 'BY', + 'CALL', + 'CAPABILITY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHAR', + 'CHAR_CONVERT', + 'CHARACTER', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'COMMENT', + 'COMMIT', + 'COMPRESSED', + 'CONFLICT', + 'CONNECT', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CUBE', + 'CURRENT', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATE', + 'DBSPACE', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DELETING', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DO', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'ELSE', + 'ELSEIF', + 'ENCRYPTED', + 'END', + 'ENDIF', + 'ESCAPE', + 'EXCEPT', + 'EXCEPTION', + 'EXEC', + 'EXECUTE', + 'EXISTING', + 'EXISTS', + 'EXTERNLOGIN', + 'FETCH', + 'FIRST', + 'FLOAT', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FORWARD', + 'FROM', + 'FULL', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTIFIED', + 'IF', + 'IN', + 'INDEX', + 'INDEX_LPAREN', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INSERTING', + 'INSTALL', + 'INSTEAD', + 'INT', + 'INTEGER', + 'INTEGRATED', + 'INTERSECT', + 'INTO', + 'IQ', + 'IS', + 'ISOLATION', + 'JOIN', + 'KERBEROS', + 'KEY', + 'LATERAL', + 'LEFT', + 'LIKE', + 'LOCK', + 'LOGIN', + 'LONG', + 'MATCH', + 'MEMBERSHIP', + 'MESSAGE', + 'MODE', + 'MODIFY', + 'NATURAL', + 'NCHAR', + 'NEW', + 'NO', + 'NOHOLDLOCK', + 'NOT', + 'NOTIFY', + 'NULL', + 'NUMERIC', + 'NVARCHAR', + 'OF', + 'OFF', + 'ON', + 'OPEN', + 'OPTION', + 'OPTIONS', + 'OR', + 'ORDER', + 'OTHERS', + 'OUT', + 'OUTER', + 'OVER', + 'PASSTHROUGH', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PRINT', + 'PRIVILEGES', + 'PROC', + 'PROCEDURE', + 'PUBLICATION', + 'RAISERROR', + 'READTEXT', + 'REAL', + 'REFERENCE', + 'REFERENCES', + 'REFRESH', + 'RELEASE', + 'REMOTE', + 'REMOVE', + 'RENAME', + 'REORGANIZE', + 'RESOURCE', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'SAVE', + 'SAVEPOINT', + 'SCROLL', + 'SELECT', + 'SENSITIVE', + 'SESSION', + 'SET', + 'SETUSER', + 'SHARE', + 'SMALLINT', + 'SOME', + 'SQLCODE', + 'SQLSTATE', + 'START', + 'STOP', + 'SUBTRANS', + 'SUBTRANSACTION', + 'SYNCHRONIZE', + 'SYNTAX_ERROR', + 'TABLE', + 'TEMPORARY', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TINYINT', + 'TO', + 'TOP', + 'TRAN', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNBOUNDED', + 'UNION', + 'UNIQUE', + 'UNIQUEIDENTIFIER', + 'UNKNOWN', + 'UNSIGNED', + 'UPDATE', + 'UPDATING', + 'USER', + 'USING', + 'VALIDATE', + 'VALUES', + 'VARBINARY', + 'VARBIT', + 'VARCHAR', + 'VARIABLE', + 'VARYING', + 'VIEW', + 'WAIT', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WINDOW', + 'WITH', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP', + 'WITHIN', + 'WORK', + 'WRITETEXT', + 'XML' + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php new file mode 100644 index 000000000..3a7434910 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2005 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2005Keywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2005'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-US/library/ms189822%28v=sql.90%29.aspx + */ + protected function getKeywords() + { + return array_merge(array_diff(parent::getKeywords(), array('DUMMY')), array( + 'EXTERNAL', + 'PIVOT', + 'REVERT', + 'SECURITYAUDIT', + 'TABLESAMPLE', + 'UNPIVOT' + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php new file mode 100644 index 000000000..38556b505 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2008 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2008Keywords extends SQLServer2005Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2008'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822%28v=sql.100%29.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'MERGE' + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php new file mode 100644 index 000000000..ada1c026b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2012 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2012Keywords extends SQLServer2008Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2012'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'SEMANTICKEYPHRASETABLE', + 'SEMANTICSIMILARITYDETAILSTABLE', + 'SEMANTICSIMILARITYTABLE', + 'TRY_CONVERT', + 'WITHIN GROUP' + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php new file mode 100644 index 000000000..fb43d2d53 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2000 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + */ +class SQLServerKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BREAK', + 'BROWSE', + 'BULK', + 'BY', + 'CASCADE', + 'CASE', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'CLUSTERED', + 'COALESCE', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'COMPUTE', + 'CONSTRAINT', + 'CONTAINS', + 'CONTAINSTABLE', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DBCC', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENY', + 'DESC', + 'DISK', + 'DISTINCT', + 'DISTRIBUTED', + 'DOUBLE', + 'DROP', + 'DUMP', + 'ELSE', + 'END', + 'ERRLVL', + 'ESCAPE', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXTERNAL', + 'FETCH', + 'FILE', + 'FILLFACTOR', + 'FOR', + 'FOREIGN', + 'FREETEXT', + 'FREETEXTTABLE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTITY', + 'IDENTITY_INSERT', + 'IDENTITYCOL', + 'IF', + 'IN', + 'INDEX', + 'INNER', + 'INSERT', + 'INTERSECT', + 'INTO', + 'IS', + 'JOIN', + 'KEY', + 'KILL', + 'LEFT', + 'LIKE', + 'LINENO', + 'LOAD', + 'NATIONAL', + 'NOCHECK ', + 'NONCLUSTERED', + 'NOT', + 'NULL', + 'NULLIF', + 'OF', + 'OFF', + 'OFFSETS', + 'ON', + 'OPEN', + 'OPENDATASOURCE', + 'OPENQUERY', + 'OPENROWSET', + 'OPENXML', + 'OPTION', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'PERCENT', + 'PIVOT', + 'PLAN', + 'PRECISION', + 'PRIMARY', + 'PRINT', + 'PROC', + 'PROCEDURE', + 'PUBLIC', + 'RAISERROR', + 'READ', + 'READTEXT', + 'RECONFIGURE', + 'REFERENCES', + 'REPLICATION', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVERT', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROWCOUNT', + 'ROWGUIDCOL', + 'RULE', + 'SAVE', + 'SCHEMA', + 'SECURITYAUDIT', + 'SELECT', + 'SESSION_USER', + 'SET', + 'SETUSER', + 'SHUTDOWN', + 'SOME', + 'STATISTICS', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'TEXTSIZE', + 'THEN', + 'TO', + 'TOP', + 'TRAN', + 'TRANSACTION', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNION', + 'UNIQUE', + 'UNPIVOT', + 'UPDATE', + 'UPDATETEXT', + 'USE', + 'USER', + 'VALUES', + 'VARYING', + 'VIEW', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITETEXT' + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php new file mode 100644 index 000000000..a162d3d53 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,168 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLite'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ABORT', + 'ACTION', + 'ADD', + 'AFTER', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ATTACH', + 'AUTOINCREMENT', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'CONFLICT', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'DATABASE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DROP', + 'EACH', + 'ELSE', + 'END', + 'ESCAPE', + 'EXCEPT', + 'EXCLUSIVE', + 'EXISTS', + 'EXPLAIN', + 'FAIL', + 'FOR', + 'FOREIGN', + 'FROM', + 'FULL', + 'GLOB', + 'GROUP', + 'HAVING', + 'IF', + 'IGNORE', + 'IMMEDIATE', + 'IN', + 'INDEX', + 'INDEXED', + 'INITIALLY', + 'INNER', + 'INSERT', + 'INSTEAD', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'KEY', + 'LEFT', + 'LIKE', + 'LIMIT', + 'MATCH', + 'NATURAL', + 'NO', + 'NOT', + 'NOTNULL', + 'NULL', + 'OF', + 'OFFSET', + 'ON', + 'OR', + 'ORDER', + 'OUTER', + 'PLAN', + 'PRAGMA', + 'PRIMARY', + 'QUERY', + 'RAISE', + 'REFERENCES', + 'REGEXP', + 'REINDEX', + 'RELEASE', + 'RENAME', + 'REPLACE', + 'RESTRICT', + 'RIGHT', + 'ROLLBACK', + 'ROW', + 'SAVEPOINT', + 'SELECT', + 'SET', + 'TABLE', + 'TEMP', + 'TEMPORARY', + 'THEN', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USING', + 'VACUUM', + 'VALUES', + 'VIEW', + 'VIRTUAL', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/MySQL57Platform.php b/wbce/include/Doctrine/DBAL/Platforms/MySQL57Platform.php new file mode 100644 index 000000000..44132a743 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/MySQL57Platform.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; + +/** + * Provides the behavior, features and SQL dialect of the MySQL 5.7 (5.7.9 GA) database platform. + * + * @author İsmail BASKIN + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Platform extends MySqlPlatform +{ + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER TABLE ' . $tableName . ' RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\MySQL57Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['json'] = Type::JSON; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/MySqlPlatform.php b/wbce/include/Doctrine/DBAL/Platforms/MySqlPlatform.php new file mode 100644 index 000000000..eb4b4ff53 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,1142 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\TextType; + +/** + * The MySqlPlatform provides the behavior, features and SQL dialect of the + * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that + * uses the InnoDB storage engine. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: MySQLPlatform + */ +class MySqlPlatform extends AbstractPlatform +{ + const LENGTH_LIMIT_TINYTEXT = 255; + const LENGTH_LIMIT_TEXT = 65535; + const LENGTH_LIMIT_MEDIUMTEXT = 16777215; + + const LENGTH_LIMIT_TINYBLOB = 255; + const LENGTH_LIMIT_BLOB = 65535; + const LENGTH_LIMIT_MEDIUMBLOB = 16777215; + + /** + * Adds MySQL-specific LIMIT clause to the query + * 18446744073709551615 is 2^64-1 maximum of unsigned BIGINT the biggest limit possible + * + * @param string $query + * @param integer $limit + * @param integer $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + } elseif ($offset !== null) { + $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * Two approaches to listing the table indexes. The information_schema is + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + if ($currentDatabase) { + $currentDatabase = $this->quoteStringLiteral($currentDatabase); + $table = $this->quoteStringLiteral($table); + + return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ". + "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ". + "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " . + "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " . + "FROM information_schema.STATISTICS WHERE TABLE_NAME = " . $table . " AND TABLE_SCHEMA = " . $currentDatabase; + } + + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + $database = $this->quoteStringLiteral($database); + + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = " . $database; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = $this->quoteStringLiteral($table); + + if (null !== $database) { + $database = $this->quoteStringLiteral($database); + } + + $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ". + "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ". + "FROM information_schema.key_column_usage k /*!50116 ". + "INNER JOIN information_schema.referential_constraints c ON ". + " c.constraint_name = k.constraint_name AND ". + " c.table_name = $table */ WHERE k.table_name = $table"; + + $databaseNameSql = null === $database ? 'DATABASE()' : $database; + + $sql .= " AND k.table_schema = $databaseNameSql /*!50116 AND c.constraint_schema = $databaseNameSql */"; + $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL"; + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'TINYINT(1)'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead. + * + * @param string $collation name of the collation + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getCollationFieldDeclaration($collation) + { + return $this->getColumnCollationDeclarationSQL($collation); + } + + /** + * {@inheritDoc} + * + * MySql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MySql supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->quoteStringLiteral($table); + + if ($database) { + $database = $this->quoteStringLiteral($database); + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ". + "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " . + "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " . $database . " AND TABLE_NAME = " . $table; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + $engine = 'INNODB'; + + if (isset($options['engine'])) { + $engine = strtoupper(trim($options['engine'])); + } + + // Propagate foreign key constraints only for InnoDB. + if (isset($options['foreignKeys']) && $engine === 'INNODB') { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + // Unset the default value if the given field definition does not allow default values. + if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { + $field['default'] = null; + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Charset + if ( ! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + + // Don't propagate default value changes for unsupported column types. + if ($columnDiff->hasChanged('default') && + count($columnDiff->changedProperties) === 1 && + ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + if (isset($diff->addedIndexes['primary'])) { + $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); + $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; + unset($diff->addedIndexes['primary']); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); + } + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex)); + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $indexClause = 'INDEX ' . $addIndex->getName(); + + if ($addIndex->isPrimary()) { + $indexClause = 'PRIMARY KEY'; + } elseif ($addIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $indexClause; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex->getQuotedColumns($this)) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $engine = 'INNODB'; + + if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { + $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); + } + + // Suppress foreign key constraint propagation on non-supporting engines. + if ('INNODB' !== $engine) { + $diff->addedForeignKeys = array(); + $diff->changedForeignKeys = array(); + $diff->removedForeignKeys = array(); + } + + $sql = array_merge( + $sql, + $this->getPreAlterTableAlterIndexForeignKeySQL($diff), + parent::getPreAlterTableIndexForeignKeySQL($diff), + $this->getPreAlterTableRenameIndexForeignKeySQL($diff) + ); + + return $sql; + } + + /** + * @param TableDiff $diff + * @param Index $index + * + * @return string[] + */ + private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index) + { + $sql = array(); + + if (! $index->isPrimary() || ! $diff->fromTable instanceof Table) { + return $sql; + } + + $tableName = $diff->getName($this)->getQuotedName($this); + + // Dropping primary keys requires to unset autoincrement attribute on the particular column first. + foreach ($index->getColumns() as $columnName) { + if (! $diff->fromTable->hasColumn($columnName)) { + continue; + } + + $column = $diff->fromTable->getColumn($columnName); + + if ($column->getAutoincrement() === true) { + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $tableName . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // original autoincrement information might be needed later on by other parts of the table alteration + $column->setAutoincrement(true); + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + // Changed primary key + if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) { + foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) { + $column = $diff->fromTable->getColumn($columnName); + + // Check if an autoincrement column was dropped from the primary key. + if ($column->getAutoincrement() && ! in_array($columnName, $changedIndex->getColumns())) { + // The autoincrement attribute needs to be removed from the dropped column + // before we can drop and recreate the primary key. + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // Restore the autoincrement attribute as it might be needed later on + // by other parts of the table alteration. + $column->setAutoincrement(true); + } + } + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return array + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) + { + if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { + return array(); + } + + $foreignKeys = array(); + /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */ + $remainingForeignKeys = array_diff_key( + $diff->fromTable->getForeignKeys(), + $diff->removedForeignKeys + ); + + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($diff->renamedIndexes as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff) + ); + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } elseif ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } elseif ($index->hasFlag('spatial')) { + $type .= 'SPATIAL '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritdoc} + */ + public function getFloatDeclarationSQL(array $field) + { + return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($field); + } + + /** + * {@inheritdoc} + */ + public function getDecimalTypeDeclarationSQL(array $columnDef) + { + return parent::getDecimalTypeDeclarationSQL($columnDef) . $this->getUnsignedDeclaration($columnDef); + } + + /** + * Get unsigned declaration for a column. + * + * @param array $columnDef + * + * @return string + */ + private function getUnsignedDeclaration(array $columnDef) + { + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $this->getUnsignedDeclaration($columnDef) . $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // mysql primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigint' => 'bigint', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'string' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'longblob' => 'blob', + 'blob' => 'blob', + 'mediumblob' => 'blob', + 'tinyblob' => 'blob', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'set' => 'simple_array', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\MySQLKeywords::class; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTemporaryTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getBlobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + return 'LONGBLOB'; + } + + /** + * {@inheritdoc} + */ + public function quoteStringLiteral($str) + { + $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped aswell. + + return parent::quoteStringLiteral($str); + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_REPEATABLE_READ; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/OraclePlatform.php b/wbce/include/Doctrine/DBAL/Platforms/OraclePlatform.php new file mode 100644 index 000000000..a0b01a853 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,1179 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BinaryType; + +/** + * OraclePlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + */ +class OraclePlatform extends AbstractPlatform +{ + /** + * Assertion for Oracle identifiers. + * + * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm + * + * @param string $identifier + * + * @throws DBALException + */ + static public function assertValidIdentifier($identifier) + { + if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { + throw new DBALException("Invalid Oracle identifier"); + } + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return "SUBSTR($value, $position, $length)"; + } + + return "SUBSTR($value, $position)"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'date': + case 'time': + case 'timestamp': + default: + return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'INSTR('.$str.', '.$substr.')'; + } + + return 'INSTR('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'SYS_GUID()'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_MONTH: + case self::DATE_INTERVAL_UNIT_QUARTER: + case self::DATE_INTERVAL_UNIT_YEAR: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + break; + + case self::DATE_INTERVAL_UNIT_YEAR: + $interval *= 12; + break; + } + + return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')'; + + default: + $calculationClause = ''; + + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + $calculationClause = '/24/60/60'; + break; + + case self::DATE_INTERVAL_UNIT_MINUTE: + $calculationClause = '/24/60'; + break; + + case self::DATE_INTERVAL_UNIT_HOUR: + $calculationClause = '/24'; + break; + + case self::DATE_INTERVAL_UNIT_WEEK: + $calculationClause = '*7'; + break; + } + + return '(' . $date . $operator . $interval . $calculationClause . ')'; + } + } + + /** + * {@inheritDoc} + * + * Note: Since Oracle timestamp differences are calculated down to the microsecond we have to truncate + * them to the difference in days. This is obviously a restriction of the original functionality, but we + * need to make this a portable function. + */ + public function getDateDiffExpression($date1, $date2) + { + return "TRUNC(TO_NUMBER(SUBSTR((" . $date1 . "-" . $date2 . "), 1, INSTR(" . $date1 . "-" . $date2 .", ' '))))"; + } + + /** + * {@inheritDoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND('.$value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . '-' . + $this->getBitAndComparisonExpression($value1, $value2) + . '+' . $value2 . ')'; + } + + /** + * {@inheritDoc} + * + * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. + * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection + * in {@see listSequences()} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() + . $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() === 0) { + return ' NOCACHE'; + } else if ($sequence->getCache() === 1) { + return ' NOCACHE'; + } else if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'NUMBER(1)'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'NUMBER(10)'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(20)'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(5)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 2000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + $database = $this->normalizeIdentifier($database); + $database = $this->quoteStringLiteral($database->getName()); + + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = " . $database; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($table, array $columns, array $options = array()) + { + $indexes = isset($options['indexes']) ? $options['indexes'] : array(); + $options['indexes'] = array(); + $sql = parent::_getCreateTableSQL($table, $columns, $options); + + foreach ($columns as $name => $column) { + if (isset($column['sequence'])) { + $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1); + } + + if (isset($column['autoincrement']) && $column['autoincrement'] || + (isset($column['autoinc']) && $column['autoinc'])) { + $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table)); + } + } + + if (isset($indexes) && ! empty($indexes)) { + foreach ($indexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $table); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = $this->normalizeIdentifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return "SELECT uind_col.index_name AS name, + ( + SELECT uind.index_type + FROM user_indexes uind + WHERE uind.index_name = uind_col.index_name + ) AS type, + decode( + ( + SELECT uind.uniqueness + FROM user_indexes uind + WHERE uind.index_name = uind_col.index_name + ), + 'NONUNIQUE', + 0, + 'UNIQUE', + 1 + ) AS is_unique, + uind_col.column_name AS column_name, + uind_col.column_position AS column_pos, + ( + SELECT ucon.constraint_type + FROM user_constraints ucon + WHERE ucon.index_name = uind_col.index_name + ) AS is_primary + FROM user_ind_columns uind_col + WHERE uind_col.table_name = " . $table . " + ORDER BY uind_col.column_position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * @param string $name + * @param string $table + * @param integer $start + * + * @return array + */ + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $tableIdentifier = $this->normalizeIdentifier($table); + $quotedTableName = $tableIdentifier->getQuotedName($this); + $unquotedTableName = $tableIdentifier->getName(); + + $nameIdentifier = $this->normalizeIdentifier($name); + $quotedName = $nameIdentifier->getQuotedName($this); + $unquotedName = $nameIdentifier->getName(); + + $sql = array(); + + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier); + + $idx = new Index($autoincrementIdentifierName, array($quotedName), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\'; + END IF; +END;'; + + $sequenceName = $this->getIdentitySequenceName( + $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, + $nameIdentifier->isQuoted() ? $quotedName : $unquotedName + ); + $sequence = new Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . ' + BEFORE INSERT + ON ' . $quotedTableName . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequence->getName() . '\'; + SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL; + WHILE (last_InsertID > last_Sequence) LOOP + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; + END LOOP; + END IF; +END;'; + + return $sql; + } + + /** + * Returns the SQL statements to drop the autoincrement for the given table name. + * + * @param string $table The table name to drop the autoincrement for. + * + * @return array + */ + public function getDropAutoincrementSql($table) + { + $table = $this->normalizeIdentifier($table); + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); + $identitySequenceName = $this->getIdentitySequenceName( + $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), + '' + ); + + return array( + 'DROP TRIGGER ' . $autoincrementIdentifierName, + $this->getDropSequenceSQL($identitySequenceName), + $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)), + ); + } + + /** + * Normalizes the given identifier. + * + * Uppercases the given identifier if it is not quoted by intention + * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. + * + * @param string $name The identifier to normalize. + * + * @return Identifier The normalized identifier. + */ + private function normalizeIdentifier($name) + { + $identifier = new Identifier($name); + + return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name)); + } + + /** + * Returns the autoincrement primary key identifier name for the given table identifier. + * + * Quotes the autoincrement primary key identifier name + * if the given table name is quoted by intention. + * + * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for. + * + * @return string + */ + private function getAutoincrementIdentifierName(Identifier $table) + { + $identifierName = $table->getName() . '_AI_PK'; + + return $table->isQuoted() + ? $this->quoteSingleIdentifier($identifierName) + : $identifierName; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + $table = $this->normalizeIdentifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return "SELECT alc.constraint_name, + alc.DELETE_RULE, + cols.column_name \"local_column\", + cols.position, + ( + SELECT r_cols.table_name + FROM user_cons_columns r_cols + WHERE alc.r_constraint_name = r_cols.constraint_name + AND r_cols.position = cols.position + ) AS \"references_table\", + ( + SELECT r_cols.column_name + FROM user_cons_columns r_cols + WHERE alc.r_constraint_name = r_cols.constraint_name + AND r_cols.position = cols.position + ) AS \"foreign_column\" + FROM user_cons_columns cols + JOIN user_constraints alc + ON alc.constraint_name = cols.constraint_name + AND alc.constraint_type = 'R' + AND alc.table_name = " . $table . " + ORDER BY cols.constraint_name ASC, cols.position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = $this->normalizeIdentifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return "SELECT * FROM user_constraints WHERE table_name = " . $table; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->normalizeIdentifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + $tabColumnsTableName = "user_tab_columns"; + $colCommentsTableName = "user_col_comments"; + $tabColumnsOwnerCondition = ''; + $colCommentsOwnerCondition = ''; + + if (null !== $database && '/' !== $database) { + $database = $this->normalizeIdentifier($database); + $database = $this->quoteStringLiteral($database->getName()); + $tabColumnsTableName = "all_tab_columns"; + $colCommentsTableName = "all_col_comments"; + $tabColumnsOwnerCondition = "AND c.owner = " . $database; + $colCommentsOwnerCondition = "AND d.OWNER = c.OWNER"; + } + + return "SELECT c.*, + ( + SELECT d.comments + FROM $colCommentsTableName d + WHERE d.TABLE_NAME = c.TABLE_NAME " . $colCommentsOwnerCondition . " + AND d.COLUMN_NAME = c.COLUMN_NAME + ) AS comments + FROM $tabColumnsTableName c + WHERE c.table_name = " . $table . " $tabColumnsOwnerCondition + ORDER BY c.column_id"; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if (! $foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = new Identifier($foreignKey); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $foreignKey = $foreignKey->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $referentialAction = null; + + if ($foreignKey->hasOption('onDelete')) { + $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $referentialAction ? ' ON DELETE ' . $referentialAction : ''; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + $action = strtoupper($action); + + switch ($action) { + case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION. + case 'NO ACTION': + // NO ACTION cannot be declared explicitly, + // therefore returning empty string to indicate to OMIT the referential clause. + return ''; + + case 'CASCADE': + case 'SET NULL': + return $action; + + default: + // SET DEFAULT is not supported, throw exception instead. + throw new \InvalidArgumentException('Invalid foreign key action: ' . $action); + } + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return 'DROP USER ' . $database . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + $fields = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Oracle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($column->getType() instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnHasChangedComment = $columnDiff->hasChanged('comment'); + + /** + * Do not add query part if only comment has changed + */ + if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { + $columnInfo = $column->toArray(); + + if ( ! $columnDiff->hasChanged('notnull')) { + unset($columnInfo['notnull']); + } + + $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo); + } + + if ($columnHasChangedComment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this); + } + + $fields = array(); + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $column->getQuotedName($this); + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritdoc} + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $notnull = ''; + + if (isset($field['notnull'])) { + $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL'; + } + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSQLDeclaration($field, $this); + $columnDef = $typeDecl . $default . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + $table = new Identifier($tableName); + + // No usage of column name to preserve BC compatibility with <2.5 + $identitySequenceName = $table->getName() . '_SEQ'; + + if ($table->isQuoted()) { + $identitySequenceName = '"' . $identitySequenceName . '"'; + } + + $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName); + + return $identitySequenceIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'oracle'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + if (preg_match('/^\s*SELECT/i', $query)) { + if (!preg_match('/\sFROM\s/i', $query)) { + $query .= " FROM dual"; + } + + $columns = array('a.*'); + + if ($offset > 0) { + $columns[] = 'ROWNUM AS doctrine_rownum'; + } + + $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query); + + if ($limit !== null) { + $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit); + } + + if ($offset > 0) { + $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1); + } + } + + return $query; + } + + /** + * {@inheritDoc} + * + * Oracle returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sP'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d 00:00:00'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return '1900-01-01 H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function fixSchemaElementName($schemaElementName) + { + if (strlen($schemaElementName) > 30) { + // Trim it + return substr($schemaElementName, 0, 30); + } + + return $schemaElementName; + } + + /** + * {@inheritDoc} + */ + public function getMaxIdentifierLength() + { + return 30; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyOnUpdate() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'integer' => 'integer', + 'number' => 'integer', + 'pls_integer' => 'boolean', + 'binary_integer' => 'boolean', + 'varchar' => 'string', + 'varchar2' => 'string', + 'nvarchar2' => 'string', + 'char' => 'string', + 'nchar' => 'string', + 'date' => 'date', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'float' => 'float', + 'binary_float' => 'float', + 'binary_double' => 'float', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'binary', + 'long raw' => 'blob', + 'rowid' => 'string', + 'urowid' => 'string', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\OracleKeywords::class; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritdoc} + */ + public function quoteStringLiteral($str) + { + $str = str_replace('\\', '\\\\', $str); // Oracle requires backslashes to be escaped aswell. + + return parent::quoteStringLiteral($str); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php new file mode 100644 index 000000000..1ba03153b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.1 database platform. + * + * @author Martin Hasoň + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Platform extends PostgreSqlPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\PostgreSQL91Keywords::class; + } + + /** + * {@inheritDoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $sql = parent::getListTableColumnsSQL($table, $database); + $parts = explode('AS complete_type,', $sql, 2); + + return $parts[0].'AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,'.$parts[1]; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php new file mode 100644 index 000000000..052b75fa4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Types\Type; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.2 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Platform extends PostgreSQL91Platform +{ + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SMALLSERIAL'; + } + + return parent::getSmallIntTypeDeclarationSQL($field); + } + + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\PostgreSQL92Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['json'] = Type::JSON; + } + + /** + * {@inheritdoc} + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + $database = $this->quoteStringLiteral($database); + + return "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $database"; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php new file mode 100644 index 000000000..68700b196 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Types\Type; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.4 database platform. + * + * @author Matteo Beccati + * @link www.doctrine-project.org + * @since 2.6 + */ +class PostgreSQL94Platform extends PostgreSQL92Platform +{ + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + if (!empty($field['jsonb'])) { + return 'JSONB'; + } + + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\PostgreSQL94Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['jsonb'] = Type::JSON; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/wbce/include/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php new file mode 100644 index 000000000..bc9fe2fa0 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,1209 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BinaryType; +use Doctrine\DBAL\Types\BigIntType; +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\IntegerType; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * @var bool + */ + private $useBooleanTrueFalseStrings = true; + + /** + * @var array PostgreSQL booleans literals + */ + private $booleanLiterals = array( + 'true' => array( + 't', + 'true', + 'y', + 'yes', + 'on', + '1' + ), + 'false' => array( + 'f', + 'false', + 'n', + 'no', + 'off', + '0' + ) + ); + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + + return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END'; + } + + return 'POSITION('.$substr.' IN '.$str.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + if (self::DATE_INTERVAL_UNIT_QUARTER === $unit) { + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + } + + return "(" . $date ." " . $operator . " (" . $interval . " || ' " . $unit . "')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'public'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsPartialIndexes() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + return $tableName . '_' . $columnName . '_seq'; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT schema_name AS nspname + FROM information_schema.schemata + WHERE schema_name NOT LIKE 'pg\_%' + AND schema_name != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + return "SELECT sequence_name AS relname, + sequence_schema AS schemaname + FROM information_schema.sequences + WHERE sequence_schema NOT LIKE 'pg\_%' + AND sequence_schema != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name + FROM information_schema.tables + WHERE table_schema NOT LIKE 'pg\_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type != 'VIEW'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT quote_ident(table_name) AS viewname, + table_schema AS schemaname, + view_definition AS definition + FROM information_schema.views + WHERE view_definition IS NOT NULL'; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE " .$this->getTableWhereClause($table) ." AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + + return "SELECT + quote_ident(relname) as relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = $table + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + )"; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid, + pg_get_expr(indpred, indrelid) AS where + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid"; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + * + * @return string + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') + { + $whereClause = $namespaceAlias.".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = $this->quoteStringLiteral($schema); + } else { + $schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + + $table = new Identifier($table); + $table = $this->quoteStringLiteral($table->getName()); + $whereClause .= "$classAlias.relname = " . $table . " AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + quote_ident(a.attname) AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM + pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_get_expr(adbin, adrelid) + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n + WHERE ".$this->getTableWhereClause($table, 'c', 'n') ." + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * Returns the SQL statement for disallowing new connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to disallow new connections for. + * + * @return string + */ + public function getDisallowDatabaseConnectionsSQL($database) + { + return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '$database'"; + } + + /** + * Returns the SQL statement for closing currently active connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to close currently active connections for. + * + * @return string + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + $database = $this->quoteStringLiteral($database); + + return "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = $database"; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + + if (($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($this->isUnchangedBinaryColumn($columnDiff)) { + continue; + } + + $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) { + $type = $column->getType(); + + // here was a server version check before, but DBAL API does not support this anymore. + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('default') || $columnDiff->hasChanged('type')) { + $defaultClause = null === $column->getDefault() + ? ' DROP DEFAULT' + : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->getName($this)->getQuotedName($this) . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query; + } else { + // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have + $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT"; + $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query; + } + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + + if ($columnDiff->hasChanged('length')) { + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSQLDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Checks whether a given column diff is a logically unchanged binary type column. + * + * Used to determine whether a column alteration for a binary type column can be skipped. + * Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType} + * are mapped to the same database column type on this platform as this platform + * does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator} + * might detect differences for binary type columns which do not have to be propagated + * to database as there actually is no difference at database level. + * + * @param ColumnDiff $columnDiff The column diff to check against. + * + * @return boolean True if the given column diff is an unchanged binary type column, false otherwise. + */ + private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) + { + $columnType = $columnDiff->column->getType(); + + if ( ! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { + return false; + } + + $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; + + if ($fromColumn) { + $fromColumnType = $fromColumn->getType(); + + if ( ! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('type', 'length', 'fixed'))) === 0; + } + + if ($columnDiff->hasChanged('type')) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('length', 'fixed'))) === 0; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Converts a single boolean value. + * + * First converts the value to its native PHP boolean type + * and passes it to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $value The value to convert. + * @param callable $callback The callback function to use for converting the real boolean value. + * + * @return mixed + * @throws \UnexpectedValueException + */ + private function convertSingleBooleanValue($value, $callback) + { + if (null === $value) { + return $callback(null); + } + + if (is_bool($value) || is_numeric($value)) { + return $callback($value ? true : false); + } + + if (!is_string($value)) { + return $callback(true); + } + + /** + * Better safe than sorry: http://php.net/in_array#106319 + */ + if (in_array(trim(strtolower($value)), $this->booleanLiterals['false'], true)) { + return $callback(false); + } + + if (in_array(trim(strtolower($value)), $this->booleanLiterals['true'], true)) { + return $callback(true); + } + + throw new \UnexpectedValueException("Unrecognized boolean literal '${value}'"); + } + + /** + * Converts one or multiple boolean values. + * + * First converts the value(s) to their native PHP boolean type + * and passes them to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $item The value(s) to convert. + * @param callable $callback The callback function to use for converting the real boolean value(s). + * + * @return mixed + */ + private function doConvertBooleans($item, $callback) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + $item[$key] = $this->convertSingleBooleanValue($value, $callback); + } + + return $item; + } + + return $this->convertSingleBooleanValue($item, $callback); + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + if (null === $boolean) { + return 'NULL'; + } + + return true === $boolean ? 'true' : 'false'; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertBooleansToDatabaseValue($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleansToDatabaseValue($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + return null === $boolean ? null : (int) $boolean; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertFromBoolean($item) + { + if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + return false; + } + + return parent::convertFromBoolean($item); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXTVAL('" . $sequenceName . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID_GENERATE_V4()'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BYTEA'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'postgresql'; + } + + /** + * {@inheritDoc} + * + * PostgreSQL returns all column names in SQL result sets in lowercase. + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); + + if ($cascade) { + $sql .= ' CASCADE'; + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'int2' => 'smallint', + 'serial' => 'integer', + 'serial4' => 'integer', + 'int' => 'integer', + 'int4' => 'integer', + 'integer' => 'integer', + 'bigserial' => 'bigint', + 'serial8' => 'bigint', + 'bigint' => 'bigint', + 'int8' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'text' => 'text', + 'tsvector' => 'text', + 'varchar' => 'string', + 'interval' => 'string', + '_varchar' => 'string', + 'char' => 'string', + 'bpchar' => 'string', + 'inet' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'time' => 'time', + 'timetz' => 'time', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'money' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'uuid' => 'guid', + 'bytea' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\PostgreSQLKeywords::class; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BYTEA'; + } + + /** + * {@inheritdoc} + */ + public function quoteStringLiteral($str) + { + $str = str_replace('\\', '\\\\', $str); // PostgreSQL requires backslashes to be escaped aswell. + + return parent::quoteStringLiteral($str); + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ($this->isSerialField($field)) { + return ''; + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + private function isSerialField(array $field) : bool + { + return $field['autoincrement'] ?? false === true && isset($field['type']) + && ($field['type'] instanceof IntegerType || $field['type'] instanceof BigIntType); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php new file mode 100644 index 000000000..e27736960 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * The SQLAnywhere11Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 11 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere11Platform extends SQLAnywherePlatform +{ + /** + * {@inheritdoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLAnywhere11Keywords::class; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php new file mode 100644 index 000000000..1b52bfee5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; + +/** + * The SQLAnywhere12Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 12 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere12Platform extends SQLAnywhere11Platform +{ + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.uP'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP WITH TIME ZONE'; + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.NEXTVAL'; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { + return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLAnywhere12Keywords::class; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['timestamp with time zone'] = 'datetime'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php new file mode 100644 index 000000000..161a4538b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\DBAL\Schema\Index; + +/** + * The SQLAnywhere16Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 16 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere16Platform extends SQLAnywhere12Platform +{ + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { + throw new UnexpectedValueException( + 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' + ); + } + + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { + return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLAnywhere16Keywords::class; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php new file mode 100644 index 000000000..a55918bc6 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -0,0 +1,1491 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 10 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywherePlatform extends AbstractPlatform +{ + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE = 1; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL = 2; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL_UNIQUE = 130; + + /** + * {@inheritdoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case $lockMode === LockMode::NONE: + return $fromClause . ' WITH (NOLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_READ: + return $fromClause . ' WITH (UPDLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_WRITE: + return $fromClause . ' WITH (XLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritdoc} + * + * SQL Anywhere supports a maximum length of 128 bytes for identifiers. + */ + public function fixSchemaElementName($schemaElementName) + { + $maxIdentifierLength = $this->getMaxIdentifierLength(); + + if (strlen($schemaElementName) > $maxIdentifierLength) { + return substr($schemaElementName, 0, $maxIdentifierLength); + } + + return $schemaElementName; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match')); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('check_on_commit') && (boolean) $foreignKey->getOption('check_on_commit')) { + $query .= ' CHECK ON COMMIT'; + } + + if ($foreignKey->hasOption('clustered') && (boolean) $foreignKey->getOption('clustered')) { + $query .= ' CLUSTERED'; + } + + if ($foreignKey->hasOption('for_olap_workload') && (boolean) $foreignKey->getOption('for_olap_workload')) { + $query .= ' FOR OLAP WORKLOAD'; + } + + return $query; + } + + /** + * {@inheritdoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + $tableSql = array(); + $alterClauses = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableAddColumnClause($column); + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableRemoveColumnClause($column); + } + + /** @var \Doctrine\DBAL\Schema\ColumnDiff $columnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $alterClause = $this->getAlterTableChangeColumnClause($columnDiff); + + if (null !== $alterClause) { + $alterClauses[] = $alterClause; + } + + if ($columnDiff->hasChanged('comment')) { + $column = $columnDiff->column; + + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameColumnClause($oldColumnName, $column); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ( ! empty($alterClauses)) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(", ", $alterClauses); + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameTableClause($diff->getNewName()); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for creating a column in a table alteration. + * + * @param Column $column The column to add. + * + * @return string + */ + protected function getAlterTableAddColumnClause(Column $column) + { + return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + /** + * Returns the SQL clause for altering a table. + * + * @param Identifier $tableName The quoted name of the table to alter. + * + * @return string + */ + protected function getAlterTableClause(Identifier $tableName) + { + return 'ALTER TABLE ' . $tableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for dropping a column in a table alteration. + * + * @param Column $column The column to drop. + * + * @return string + */ + protected function getAlterTableRemoveColumnClause(Column $column) + { + return 'DROP ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a column in a table alteration. + * + * @param string $oldColumnName The quoted name of the column to rename. + * @param Column $column The column to rename to. + * + * @return string + */ + protected function getAlterTableRenameColumnClause($oldColumnName, Column $column) + { + $oldColumnName = new Identifier($oldColumnName); + + return 'RENAME ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a table in a table alteration. + * + * @param Identifier $newTableName The quoted name of the table to rename to. + * + * @return string + */ + protected function getAlterTableRenameTableClause(Identifier $newTableName) + { + return 'RENAME ' . $newTableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for altering a column in a table alteration. + * + * This method returns null in case that only the column comment has changed. + * Changes in column comments have to be handled differently. + * + * @param ColumnDiff $columnDiff The diff of the column to alter. + * + * @return string|null + */ + protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff) + { + $column = $columnDiff->column; + + // Do not return alter clause if only comment has changed. + if ( ! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) { + $columnAlterationClause = 'ALTER ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + if ($columnDiff->hasChanged('default') && null === $column->getDefault()) { + $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT'; + } + + return $columnAlterationClause; + } + } + + /** + * {@inheritdoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'BIGINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'LONG BINARY'; + } + + /** + * {@inheritdoc} + * + * BIT type columns require an explicit NULL declaration + * in SQL Anywhere if they shall be nullable. + * Otherwise by just omitting the NOT NULL clause, + * SQL Anywhere will declare them NOT NULL nonetheless. + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + $nullClause = isset($columnDef['notnull']) && (boolean) $columnDef['notnull'] === false ? ' NULL' : ''; + + return 'BIT' . $nullClause; + } + + /** + * {@inheritdoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . '.' . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritdoc} + */ + public function getConcatExpression() + { + return 'STRING(' . implode(', ', (array) func_get_args()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getCreateForeignKeySQL($constraint, $table); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . + ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCreateDatabaseSQL($database) + { + $database = new Identifier($database); + + return "CREATE DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + * + * Appends SQL Anywhere specific flags if given. + */ + public function getCreateIndexSQL(Index $index, $table) + { + return parent::getCreateIndexSQL($index, $table). $this->getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE'; + } + + /** + * {@inheritdoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritdoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT TIMESTAMP'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritdoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_UNCOMMITTED; + } + + /** + * {@inheritdoc} + */ + public function getDropDatabaseSQL($database) + { + $database = new Identifier($database); + + return "DROP DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } + + if ( ! is_string($index)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . + '\Doctrine\DBAL\Schema\Index.' + ); + } + + if ( ! isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + if ( ! is_string($table)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . + '\Doctrine\DBAL\Schema\Table.' + ); + } + + return 'DROP INDEX ' . $table . '.' . $index; + } + + /** + * {@inheritdoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + $foreignKeyName = $foreignKey->getName(); + $localColumns = $foreignKey->getQuotedLocalColumns($this); + $foreignColumns = $foreignKey->getQuotedForeignColumns($this); + $foreignTableName = $foreignKey->getQuotedForeignTableName($this); + + if ( ! empty($foreignKeyName)) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + + if (empty($localColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + + if (empty($foreignColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + + if (empty($foreignTableName)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + if ($foreignKey->hasOption('notnull') && (boolean) $foreignKey->getOption('notnull')) { + $sql .= 'NOT NULL '; + } + + return $sql . + 'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' . + 'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) . + ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')'; + } + + /** + * Returns foreign key MATCH clause for given type. + * + * @param integer $type The foreign key match type + * + * @return string + * + * @throws \InvalidArgumentException if unknown match type given + */ + public function getForeignKeyMatchClauseSQL($type) + { + switch ((int) $type) { + case self::FOREIGN_KEY_MATCH_SIMPLE: + return 'SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL: + return 'FULL'; + break; + case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE: + return 'UNIQUE SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL_UNIQUE: + return 'UNIQUE FULL'; + default: + throw new \InvalidArgumentException('Invalid foreign key match type: ' . $type); + } + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // NO ACTION is not supported, therefore falling back to RESTRICT. + if (strtoupper($action) === 'NO ACTION') { + return 'RESTRICT'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritdoc} + */ + public function getForUpdateSQL() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritdoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritdoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritdoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'INT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT db_name(number) AS name FROM sa_db_list()'; + } + + /** + * {@inheritdoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $user = 'USER_NAME()'; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = $this->quoteStringLiteral($user); + } + + return "SELECT col.column_name, + COALESCE(def.user_type_name, def.domain_name) AS 'type', + def.declared_width AS 'length', + def.scale, + CHARINDEX('unsigned', def.domain_name) AS 'unsigned', + IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull', + col.\"default\", + def.is_autoincrement AS 'autoincrement', + rem.remarks AS 'comment' + FROM sa_describe_query('SELECT * FROM \"$table\"') AS def + JOIN SYS.SYSTABCOL AS col + ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id + LEFT JOIN SYS.SYSREMARK AS rem + ON col.object_id = rem.object_id + WHERE def.base_owner_name = $user + ORDER BY def.base_column_id ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListTableConstraintsSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = $this->quoteStringLiteral($user); + $table = $this->quoteStringLiteral($table); + } else { + $table = $this->quoteStringLiteral($table); + } + + return "SELECT con.* + FROM SYS.SYSCONSTRAINT AS con + JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id + WHERE tab.table_name = $table + AND tab.creator = USER_ID($user)"; + } + + /** + * {@inheritdoc} + */ + public function getListTableForeignKeysSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = $this->quoteStringLiteral($user); + $table = $this->quoteStringLiteral($table); + } else { + $table = $this->quoteStringLiteral($table); + } + + return "SELECT fcol.column_name AS local_column, + ptbl.table_name AS foreign_table, + pcol.column_name AS foreign_column, + idx.index_name, + IF fk.nulls = 'N' + THEN 1 + ELSE NULL + ENDIF AS notnull, + CASE ut.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE dt.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete, + IF fk.check_on_commit = 'Y' + THEN 1 + ELSE NULL + ENDIF AS check_on_commit, -- check_on_commit flag + IF ftbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF fk.match_type = 0 + THEN NULL + ELSE fk.match_type + ENDIF AS 'match', -- match option + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSFKEY AS fk + JOIN SYS.SYSIDX AS idx + ON fk.foreign_table_id = idx.table_id + AND fk.foreign_index_id = idx.index_id + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSTAB AS ptbl + ON fk.primary_table_id = ptbl.table_id + JOIN SYS.SYSTAB AS ftbl + ON fk.foreign_table_id = ftbl.table_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id + AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS pcol + ON ptbl.table_id = pcol.table_id + AND idxcol.primary_column_id = pcol.column_id + JOIN SYS.SYSTABCOL AS fcol + ON ftbl.table_id = fcol.table_id + AND idxcol.column_id = fcol.column_id + LEFT JOIN SYS.SYSTRIGGER ut + ON fk.foreign_table_id = ut.foreign_table_id + AND fk.foreign_index_id = ut.foreign_key_id + AND ut.event = 'C' + LEFT JOIN SYS.SYSTRIGGER dt + ON fk.foreign_table_id = dt.foreign_table_id + AND fk.foreign_index_id = dt.foreign_key_id + AND dt.event = 'D' + WHERE ftbl.table_name = $table + AND ftbl.creator = USER_ID($user) + ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = $this->quoteStringLiteral($user); + $table = $this->quoteStringLiteral($table); + } else { + $table = $this->quoteStringLiteral($table); + } + + return "SELECT idx.index_name AS key_name, + IF idx.index_category = 1 + THEN 1 + ELSE 0 + ENDIF AS 'primary', + col.column_name, + IF idx.\"unique\" IN(1, 2, 5) + THEN 0 + ELSE 1 + ENDIF AS non_unique, + IF tbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF idx.\"unique\" = 5 + THEN 1 + ELSE NULL + ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSIDX AS idx + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS col + ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id + JOIN SYS.SYSTAB AS tbl + ON idx.table_id = tbl.table_id + WHERE tbl.table_name = $table + AND tbl.creator = USER_ID($user) + AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints + ORDER BY idx.index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTablesSQL() + { + return "SELECT tbl.table_name + FROM SYS.SYSTAB AS tbl + JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id + WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP' + AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + AND obj.type = 'U' -- user created tables only + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListUsersSQL() + { + return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC'; + } + + /** + * {@inheritdoc} + */ + public function getListViewsSQL($database) + { + return "SELECT tbl.table_name, v.view_def + FROM SYS.SYSVIEW v + JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id + JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id + WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $str . ', ' . $substr . ')'; + } + + return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')'; + } + + /** + * {@inheritdoc} + */ + public function getMaxIdentifierLength() + { + return 128; + } + + /** + * {@inheritdoc} + */ + public function getMd5Expression($column) + { + return "HASH(" . $column . ", 'MD5')"; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a primary key + * declaration to be used in statements like ALTER TABLE. + * + * @param Index $index Index definition + * @param string $name Name of the primary key + * + * @return string DBMS specific SQL code portion needed to set a primary key + * + * @throws \InvalidArgumentException if the given index is not a primary key. + */ + public function getPrimaryKeyDeclarationSQL(Index $index, $name = null) + { + if ( ! $index->isPrimary()) { + throw new \InvalidArgumentException( + 'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'SMALLINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Returns the SQL statement for starting an existing database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement after having created a new database + * as it has to be explicitly started to be usable. + * SQL Anywhere does not automatically start a database after creation! + * + * @param string $database Name of the database to start. + * + * @return string + */ + public function getStartDatabaseSQL($database) + { + $database = new Identifier($database); + + return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF"; + } + + /** + * Returns the SQL statement for stopping a running database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement before dropping an existing database + * as it has to be explicitly stopped before it can be dropped. + * + * @param string $database Name of the database to stop. + * + * @return string + */ + public function getStopDatabaseSQL($database) + { + $database = new Identifier($database); + + return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY'; + } + + /** + * {@inheritdoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (null === $length) { + return 'SUBSTRING(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritdoc} + */ + public function getTemporaryTableSQL() + { + return 'GLOBAL TEMPORARY'; + } + + /** + * {@inheritdoc} + */ + public function getTimeFormatString() + { + return 'H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + return $this->getLtrimExpression($str); + case self::TRIM_TRAILING: + return $this->getRtrimExpression($str); + default: + return 'TRIM(' . $str . ')'; + } + } + + $pattern = "'%[^' + $char + ']%'"; + + switch ($pos) { + case self::TRIM_LEADING: + return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))'; + case self::TRIM_TRAILING: + return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))'; + default: + return + 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' . + 'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))'; + } + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * {@inheritdoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if ($index->isPrimary()) { + throw new \InvalidArgumentException( + 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().' + ); + } + + if ( ! $index->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create unique constraint declarations, no common index declarations with ' . + 'getUniqueConstraintDeclarationSQL().' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getVarcharDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getVarcharMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $unsigned = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : ''; + $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : ''; + + return $unsigned . $columnDef['integer_type'] . $autoincrement; + } + + /** + * {@inheritdoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + $indexSql = array(); + + if ( ! empty($options['uniqueConstraints'])) { + foreach ((array) $options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if ( ! empty($options['indexes'])) { + /** @var \Doctrine\DBAL\Schema\Index $index */ + foreach ((array) $options['indexes'] as $index) { + $indexSql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if ( ! empty($options['primary'])) { + $flags = ''; + + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) { + $flags = ' CLUSTERED '; + } + + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')'; + } + + if ( ! empty($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + $check = $this->getCheckDeclarationSQL($columns); + + if ( ! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + return array_merge(array($query), $indexSql); + } + + /** + * {@inheritdoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case Connection::TRANSACTION_READ_COMMITTED: + return 1; + case Connection::TRANSACTION_REPEATABLE_READ: + return 2; + case Connection::TRANSACTION_SERIALIZABLE: + return 3; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + /** + * {@inheritdoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + $limitOffsetClause = ''; + + if ($limit > 0) { + $limitOffsetClause = 'TOP ' . $limit . ' '; + } + + if ($offset > 0) { + if ($limit == 0) { + $limitOffsetClause = 'TOP ALL '; + } + + $limitOffsetClause .= 'START AT ' . ($offset + 1) . ' '; + } + + if ($limitOffsetClause) { + return preg_replace('/^\s*(SELECT\s+(DISTINCT\s+)?)/i', '\1' . $limitOffsetClause, $query); + } + + return $query; + } + + /** + * Return the INDEX query section dealing with non-standard + * SQL Anywhere options. + * + * @param Index $index Index definition + * + * @return string + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + $sql = ''; + + if ( ! $index->isPrimary() && $index->hasFlag('for_olap_workload')) { + $sql .= ' FOR OLAP WORKLOAD'; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')' + : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'; + } + + /** + * Returns the SQL snippet for creating a table constraint. + * + * @param Constraint $constraint The table constraint to create the SQL snippet for. + * @param string|null $name The table constraint name to use if any. + * + * @return string + * + * @throws \InvalidArgumentException if the given table constraint type is not supported by this method. + */ + protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getForeignKeyDeclarationSQL($constraint); + } + + if ( ! $constraint instanceof Index) { + throw new \InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint)); + } + + if ( ! $constraint->isPrimary() && ! $constraint->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' . + 'with getTableConstraintDeclarationSQL().' + ); + } + + $constraintColumns = $constraint->getQuotedColumns($this); + + if (empty($constraintColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + $sql = ''; + $flags = ''; + + if ( ! empty($name)) { + $name = new Identifier($name); + $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' '; + } + + if ($constraint->hasFlag('clustered')) { + $flags = 'CLUSTERED '; + } + + if ($constraint->isPrimary()) { + return $sql . 'PRIMARY KEY ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + return $sql . 'UNIQUE ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->hasFlag('virtual')) { + $type .= 'VIRTUAL '; + } + + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } + + return $type; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLAnywhereKeywords::class; + } + + /** + * {@inheritdoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')'); + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'char' => 'string', + 'long nvarchar' => 'text', + 'long varchar' => 'text', + 'nchar' => 'string', + 'ntext' => 'text', + 'nvarchar' => 'string', + 'text' => 'text', + 'uniqueidentifierstr' => 'guid', + 'varchar' => 'string', + 'xml' => 'text', + 'bigint' => 'bigint', + 'unsigned bigint' => 'bigint', + 'bit' => 'boolean', + 'decimal' => 'decimal', + 'double' => 'float', + 'float' => 'float', + 'int' => 'integer', + 'integer' => 'integer', + 'unsigned int' => 'integer', + 'numeric' => 'decimal', + 'smallint' => 'smallint', + 'unsigned smallint', 'smallint', + 'tinyint' => 'smallint', + 'unsigned tinyint', 'smallint', + 'money' => 'decimal', + 'smallmoney' => 'decimal', + 'long varbit' => 'text', + 'varbit' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'smalldatetime' => 'datetime', + 'time' => 'time', + 'timestamp' => 'datetime', + 'binary' => 'binary', + 'image' => 'blob', + 'long binary' => 'blob', + 'uniqueidentifier' => 'guid', + 'varbinary' => 'binary', + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLAzurePlatform.php new file mode 100644 index 000000000..71829cd0e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Table; + +/** + * Platform to ensure compatibility of Doctrine with SQL Azure + * + * On top of SQL Server 2008 the following functionality is added: + * + * - Create tables with the FEDERATED ON syntax. + */ +class SQLAzurePlatform extends SQLServer2008Platform +{ + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) + { + $sql = parent::getCreateTableSQL($table, $createFlags); + + if ($table->hasOption('azure.federatedOnColumnName')) { + $distributionName = $table->getOption('azure.federatedOnDistributionName'); + $columnName = $table->getOption('azure.federatedOnColumnName'); + $stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')'; + + $sql[0] = $sql[0] . $stmt; + } + + return $sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2005Platform.php new file mode 100644 index 000000000..af42f5166 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2005 version and + * higher. + * + * Differences to SQL Server 2008 are: + * + * - DATETIME2 datatype does not exist, only DATETIME which has a precision of + * 3. This is not supported by PHP DateTime, so we are emulating it by + * setting .000 manually. + * - Starting with SQLServer2005 VARCHAR(MAX), VARBINARY(MAX) and + * NVARCHAR(max) replace the old TEXT, NTEXT and IMAGE types. See + * {@link http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx} + * for more information. + */ +class SQLServer2005Platform extends SQLServerPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2005 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLServer2005Keywords::class; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLServer2008Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2008Platform.php new file mode 100644 index 000000000..d9f308567 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2008 version. + * + * Differences to SQL Server 2005 and before are that a new DATETIME2 type was + * introduced that has a higher precision. + */ +class SQLServer2008Platform extends SQLServer2005Platform +{ + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIMEOFFSET(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + * + * Adding Datetime2 Type + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['datetime2'] = 'datetime'; + $this->doctrineTypeMapping['date'] = 'date'; + $this->doctrineTypeMapping['time'] = 'time'; + $this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2008 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLServer2008Keywords::class; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2012Platform.php new file mode 100644 index 000000000..4cdff77bf --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLServer2012Platform.php @@ -0,0 +1,158 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Sequence; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2012 version. + * + * Differences to SQL Server 2008 and before are that sequences are introduced, + * and support for the new OFFSET... FETCH syntax for result pagination has been added. + * + * @author Steve Müller + */ +class SQLServer2012Platform extends SQLServer2008Platform +{ + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, + CAST( + seq.increment AS VARCHAR(MAX) + ) AS increment, -- CAST avoids driver error for sql_variant type + CAST( + seq.start_value AS VARCHAR(MAX) + ) AS start_value -- CAST avoids driver error for sql_variant type + FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT NEXT VALUE FOR ' . $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2012 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLServer2012Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + // Queries using OFFSET... FETCH MUST have an ORDER BY clause + // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement + // but can be in a newline + $matches = array(); + $matchesCount = preg_match_all("/[\\s]+order\\s+by\\s/im", $query, $matches, PREG_OFFSET_CAPTURE); + $orderByPos = false; + if ($matchesCount > 0) { + $orderByPos = $matches[0][($matchesCount - 1)][1]; + } + + if ($orderByPos === false + || substr_count($query, "(", $orderByPos) - substr_count($query, ")", $orderByPos) + ) { + if (preg_match('/^SELECT\s+DISTINCT/im', $query)) { + // SQL Server won't let us order by a non-selected column in a DISTINCT query, + // so we have to do this madness. This says, order by the first column in the + // result. SQL Server's docs say that a nonordered query's result order is non- + // deterministic anyway, so this won't do anything that a bunch of update and + // deletes to the table wouldn't do anyway. + $query .= " ORDER BY 1"; + } else { + // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you + // use constant expressions in the order by list. + $query .= " ORDER BY (SELECT 0)"; + } + } + + if ($offset === null) { + $offset = 0; + } + + // This looks somewhat like MYSQL, but limit/offset are in inverse positions + // Supposedly SQL:2008 core standard. + // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. + $query .= " OFFSET " . (int) $offset . " ROWS"; + + if ($limit !== null) { + $query .= " FETCH NEXT " . (int) $limit . " ROWS ONLY"; + } + + return $query; + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/wbce/include/Doctrine/DBAL/Platforms/SQLServerPlatform.php new file mode 100644 index 000000000..08a454c5b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,1629 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * The SQLServerPlatform provides the behavior, features and SQL dialect of the + * Microsoft SQL Server database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @author Steve Müller + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + public function getCurrentDateSQL() + { + return $this->getConvertExpression('date', 'GETDATE()'); + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return $this->getConvertExpression('time', 'GETDATE()'); + } + + /** + * Returns an expression that converts an expression of one data type to another. + * + * @param string $dataType The target native data type. Alias data types cannot be used. + * @param string $expression The SQL expression to convert. + * + * @return string + */ + private function getConvertExpression($dataType, $expression) + { + return sprintf('CONVERT(%s, %s)', $dataType, $expression); + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'dbo'; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if (! $foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = new Identifier($foreignKey); + } + + if (! $table instanceof Table) { + $table = new Identifier($table); + } + + $foreignKey = $foreignKey->getQuotedName($this); + $table = $table->getQuotedName($this); + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if (!isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index') + ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . " + ELSE + DROP INDEX " . $index . " ON " . $table; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $defaultConstraintsSql = array(); + $commentsSql = array(); + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (isset($column['primary']) && $column['primary']) { + $column['notnull'] = true; + } + + // Build default constraints SQL statements. + if (isset($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $tableName . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $column); + } + + if ( ! empty($column['comment']) || is_numeric($column['comment'])) { + $commentsSql[] = $this->getCreateColumnCommentSQL($tableName, $column['name'], $column['comment']); + } + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && !empty($options['primary'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (!empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && !empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return array_merge($sql, $commentsSql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + $flags = ''; + if ($index->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL statement for creating a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param array $column Column definition. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if ( ! isset($column['default'])) { + throw new \InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + $columnName = new Identifier($column['name']); + + return + ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $columnName->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); + + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + + return $constraint; + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && !$index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } elseif ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + + foreach ($index->getQuotedColumns($this) as $field) { + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + $commentsSql = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default'])) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + + $comment = $this->getColumnComment($column); + + if ( ! empty($comment) || is_numeric($comment)) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $comment = $this->getColumnComment($column); + $hasComment = ! empty ($comment) || is_numeric($comment); + + if ($columnDiff->fromColumn instanceof Column) { + $fromComment = $this->getColumnComment($columnDiff->fromColumn); + $hasFromComment = ! empty ($fromComment) || is_numeric($fromComment); + + if ($hasFromComment && $hasComment && $fromComment != $comment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } elseif ($hasFromComment && ! $hasComment) { + $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); + } elseif ($hasComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } else { + // todo: Original comment cannot be determined. What to do? Add, update, drop or skip? + } + + // Do not add query part if only comment has changed. + if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { + continue; + } + + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + + if ($requireDropDefaultConstraint) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $columnDiff->oldColumnName + ); + } + + $columnDef = $column->toArray(); + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default']) && ($requireDropDefaultConstraint || $columnDiff->hasChanged('default'))) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = "sp_RENAME '" . + $diff->getName($this)->getQuotedName($this) . "." . $oldColumnName->getQuotedName($this) . + "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; + + // Recreate default constraint with new column name if necessary (for future reference). + if ($column->getDefault() !== null) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $oldColumnName->getQuotedName($this) + ); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + $sql = array_merge($sql, $commentsSql); + + if ($diff->newName !== false) { + $sql[] = "sp_RENAME '" . $diff->getName($this)->getQuotedName($this) . "', '" . $diff->getNewName()->getName() . "'"; + + /** + * Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. + */ + $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . + "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . + "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . + "'" . $this->generateIdentifierName($diff->newName) . "') + ''', ''OBJECT'';' " . + "FROM sys.default_constraints dc " . + "JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " . + "WHERE tbl.name = '" . $diff->getNewName()->getName() . "';" . + "EXEC sp_executesql @sql"; + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param Column $column The column to generate the clause for. + * + * @return string + */ + private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) + { + $columnDef = $column->toArray(); + $columnDef['name'] = $column->getQuotedName($this); + + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + } + + /** + * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param string $columnName The name of the column to generate the clause for. + * + * @return string + */ + private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) + { + return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + } + + /** + * Checks whether a column alteration requires dropping its default constraint first. + * + * Different to other database vendors SQL Server implements column default values + * as constraints and therefore changes in a column's default value as well as changes + * in a column's type require dropping the default constraint first before being to + * alter the particular column to the new definition. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return boolean True if the column alteration requires dropping its default constraint first, false otherwise. + */ + private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) + { + // We can only decide whether to drop an existing default constraint + // if we know the original default value. + if ( ! $columnDiff->fromColumn instanceof Column) { + return false; + } + + // We only need to drop an existing default constraint if we know the + // column was defined with a default value before. + if ($columnDiff->fromColumn->getDefault() === null) { + return false; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and it has changed. + if ($columnDiff->hasChanged('default')) { + return true; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and the native column type has changed. + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed')) { + return true; + } + + return false; + } + + /** + * Returns the SQL statement for altering a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getUpdateExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL statement for dropping a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to drop the comment for. + * + * @return string + */ + protected function getDropColumnCommentSQL($tableName, $columnName) + { + return $this->getDropExtendedPropertySQL( + 'MS_Description', + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + sprintf( + "EXEC sp_RENAME N'%s.%s', N'%s', N'INDEX'", + $tableName, + $oldIndexName, + $index->getQuotedName($this) + ) + ); + } + + /** + * Returns the SQL statement for adding an extended property to a database object. + * + * @param string $name The name of the property to add. + * @param string|null $value The value of the property to add. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx + */ + public function getAddExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_addextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for dropping an extended property from a database object. + * + * @param string $name The name of the property to drop. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx + */ + public function getDropExtendedPropertySQL( + $name, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_dropextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for updating an extended property of a database object. + * + * @param string $name The name of the property to update. + * @param string|null $value The value of the property to update. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx + */ + public function getUpdateExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_updateextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation, + CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + JOIN sys.schemas AS scm + ON obj.schema_id = scm.schema_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + LEFT JOIN sys.extended_properties AS prop + ON obj.object_id = prop.major_id + AND col.column_id = prop.minor_id + AND prop.name = 'MS_Description' + WHERE obj.type = 'U' + AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id + WHERE " . + $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)'); + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id + JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id + WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . " + ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + * + * @return string + */ + private function getTableWhereClause($table, $schemaColumn, $tableColumn) + { + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = $this->quoteStringLiteral($schema); + $table = $this->quoteStringLiteral($table); + } else { + $schema = "SCHEMA_NAME()"; + $table = $this->quoteStringLiteral($table); + } + + return "({$tableColumn} = {$table} AND {$schemaColumn} = {$schema})"; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + /** Original query used to get those expressions + declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x'; + declare @pat varchar(10) = '%[^' + @trim_char + ']%'; + select @c as string + , @trim_char as trim_char + , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading + , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing + , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both; + */ + $pattern = "'%[^' + $char + ']%'"; + + if ($pos == self::TRIM_LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } + + if ($pos == self::TRIM_TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return '(' . implode(' + ', $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT * FROM sys.databases'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (!is_null($length)) { + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 8000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return (!empty($columnDef['autoincrement'])) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null) { + return $query; + } + + $start = $offset + 1; + $end = $offset + $limit; + + // We'll find a SELECT or SELECT distinct and prepend TOP n to it + // Even if the TOP n is very large, the use of a CTE will + // allow the SQL Server query planner to optimize it so it doesn't + // actually scan the entire range covered by the TOP clause. + $selectPattern = '/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/im'; + $replacePattern = sprintf('$1%s $2', "TOP $end"); + $query = preg_replace($selectPattern, $replacePattern, $query); + + if (stristr($query, "ORDER BY")) { + // Inner order by is not valid in SQL Server for our purposes + // unless it's in a TOP N subquery. + $query = $this->scrubInnerOrderBy($query); + } + + // Build a new limited query around the original, using a CTE + return sprintf( + "WITH dctrn_cte AS (%s) " + . "SELECT * FROM (" + . "SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte" + . ") AS doctrine_tbl " + . "WHERE doctrine_rownum BETWEEN %d AND %d ORDER BY doctrine_rownum ASC", + $query, + $start, + $end + ); + } + + /** + * Remove ORDER BY clauses in subqueries - they're not supported by SQL Server. + * Caveat: will leave ORDER BY in TOP N subqueries. + * + * @param $query + * @return string + */ + private function scrubInnerOrderBy($query) + { + $count = substr_count(strtoupper($query), 'ORDER BY'); + $offset = 0; + + while ($count-- > 0) { + $orderByPos = stripos($query, ' ORDER BY', $offset); + if ($orderByPos === false) { + break; + } + + $qLen = strlen($query); + $parenCount = 0; + $currentPosition = $orderByPos; + + while ($parenCount >= 0 && $currentPosition < $qLen) { + if ($query[$currentPosition] === '(') { + $parenCount++; + } elseif ($query[$currentPosition] === ')') { + $parenCount--; + } + + $currentPosition++; + } + + if ($this->isOrderByInTopNSubquery($query, $orderByPos)) { + // If the order by clause is in a TOP N subquery, do not remove + // it and continue iteration from the current position. + $offset = $currentPosition; + continue; + } + + if ($currentPosition < $qLen - 1) { + $query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1); + $offset = $orderByPos; + } + } + return $query; + } + + /** + * Check an ORDER BY clause to see if it is in a TOP N query or subquery. + * + * @param string $query The query + * @param int $currentPosition Start position of ORDER BY clause + * @return bool true if ORDER BY is in a TOP N query, false otherwise + */ + private function isOrderByInTopNSubquery($query, $currentPosition) + { + // Grab query text on the same nesting level as the ORDER BY clause we're examining. + $subQueryBuffer = ''; + $parenCount = 0; + + // If $parenCount goes negative, we've exited the subquery we're examining. + // If $currentPosition goes negative, we've reached the beginning of the query. + while ($parenCount >= 0 && $currentPosition >= 0) { + if ($query[$currentPosition] === '(') { + $parenCount--; + } elseif ($query[$currentPosition] === ')') { + $parenCount++; + } + + // Only yank query text on the same nesting level as the ORDER BY clause. + $subQueryBuffer = ($parenCount === 0 ? $query[$currentPosition] : ' ') . $subQueryBuffer; + + $currentPosition--; + } + + if (preg_match('/SELECT\s+(DISTINCT\s+)?TOP\s/i', $subQueryBuffer)) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 1 : 0; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 1 : 0; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'bigint' => 'bigint', + 'numeric' => 'decimal', + 'bit' => 'boolean', + 'smallint' => 'smallint', + 'decimal' => 'decimal', + 'smallmoney' => 'integer', + 'int' => 'integer', + 'tinyint' => 'smallint', + 'money' => 'integer', + 'float' => 'float', + 'real' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'image' => 'blob', + 'uniqueidentifier' => 'guid', + ); + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // RESTRICT is not supported, therefore falling back to NO ACTION. + if (strtoupper($action) === 'RESTRICT') { + return 'NO ACTION'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritDoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case LockMode::NONE === $lockMode: + return $fromClause . ' WITH (NOLOCK)'; + + case LockMode::PESSIMISTIC_READ === $lockMode: + return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; + + case LockMode::PESSIMISTIC_WRITE === $lockMode: + return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLServerKeywords::class; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return "[" . str_replace("]", "][", $str) . "]"; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + + return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! isset($field['default'])) { + return empty($field['notnull']) ? ' NULL' : ''; + } + + if ( ! isset($field['type'])) { + return " DEFAULT '" . $field['default'] . "'"; + } + + $type = (string) $field['type']; + + if (in_array($type, ['Integer', 'BigInt', 'SmallInt'], true)) { + return " DEFAULT " . $field['default']; + } + + if (in_array($type, ['DateTime', 'DateTimeTz', 'DateTimeImmutable', 'DateTimeTzImmutable'], true) && $field['default'] === $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if ($type === 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + + return " DEFAULT '" . $field['default'] . "'"; + } + + /** + * {@inheritdoc} + * + * Modifies column declaration order as it differs in Microsoft SQL Server. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSQLDeclaration($field, $this); + $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + * + * @return string + */ + private function generateDefaultConstraintName($table, $column) + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + * + * @return string + */ + private function generateIdentifierName($identifier) + { + // Always generate name for unquoted identifiers to ensure consistency. + $identifier = new Identifier($identifier); + + return strtoupper(dechex(crc32($identifier->getName()))); + } +} diff --git a/wbce/include/Doctrine/DBAL/Platforms/SqlitePlatform.php b/wbce/include/Doctrine/DBAL/Platforms/SqlitePlatform.php new file mode 100644 index 000000000..f52e384d8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,1134 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Martin Hasoň + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " + . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'time': + return 'time(\'now\')'; + case 'date': + return 'date(\'now\')'; + case 'timestamp': + default: + return 'datetime(\'now\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimChar = ($char != false) ? (', ' . $char) : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + $trimFn = 'TRIM'; + } + + return $trimFn . '(' . $str . $trimChar . ')'; + } + + /** + * {@inheritDoc} + * + * SQLite only supports the 2 parameter variant of this function + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE('.$str.', '.$substr.')'; + } + + return 'LOCATE('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + case self::DATE_INTERVAL_UNIT_MINUTE: + case self::DATE_INTERVAL_UNIT_HOUR: + return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + + default: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + } + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 1; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + // sqlite autoincrement is implicit for integer PKs, but not when the field is unsigned + if ( ! empty($columnDef['autoincrement'])) { + return ''; + } + + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( + $foreignKey->getQuotedLocalColumns($this), + str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)), + $foreignKey->getQuotedForeignColumns($this), + $foreignKey->getName(), + $foreignKey->getOptions() + )); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace('.', '__', $name); + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $foreignKey) { + $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey); + } + } + + $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['alter']) && true === $options['alter']) { + return $query; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + return $query; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BLOB'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = str_replace('.', '__', $table); + $table = $this->quoteStringLiteral($table); + + return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = $table AND sql NOT NULL ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + $table = $this->quoteStringLiteral($table); + + return "PRAGMA table_info($table)"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + $table = $this->quoteStringLiteral($table); + + return "PRAGMA index_list($table)"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type = 'table' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE'; + $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE'); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'sqlite'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableIdentifier = new Identifier($tableName); + $tableName = str_replace('.', '__', $tableIdentifier->getQuotedName($this)); + + return 'DELETE FROM ' . $tableName; + } + + /** + * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). + * + * @param integer|float $value + * + * @return float + */ + static public function udfSqrt($value) + { + return sqrt($value); + } + + /** + * User-defined function for Sqlite that implements MOD(a, b). + * + * @param integer $a + * @param integer $b + * + * @return integer + */ + static public function udfMod($a, $b) + { + return ($a % $b); + } + + /** + * @param string $str + * @param string $substr + * @param integer $offset + * + * @return integer + */ + static public function udfLocate($str, $substr, $offset = 0) + { + // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. + // So we have to make them compatible if an offset is given. + if ($offset > 0) { + $offset -= 1; + } + + $pos = strpos($str, $substr, $offset); + + if ($pos !== false) { + return $pos + 1; + } + + return 0; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSql() + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function getInlineColumnCommentSQL($comment) + { + return '--' . str_replace("\n", "\n--", $comment) . "\n"; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'serial' => 'integer', + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'clob' => 'text', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'longvarchar' => 'string', + 'varchar2' => 'string', + 'nvarchar' => 'string', + 'image' => 'string', + 'ntext' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return Keywords\SQLiteKeywords::class; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + foreach ($diff->fromTable->getIndexes() as $index) { + if ( ! $index->isPrimary()) { + $sql[] = $this->getDropIndexSQL($index, $diff->name); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this); + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + continue; + } + + $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if (null === $limit && null !== $offset) { + return $query . ' LIMIT -1 OFFSET ' . $offset; + } + + return parent::doModifyLimitQuery($query, $limit, $offset); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + $tableName = str_replace('.', '__', $tableName); + + return $tableName; + } + + /** + * {@inheritDoc} + * + * Sqlite Platform emulates schema by underscoring each dot and generating tables + * into the default database. + * + * This hack is implemented to be able to use SQLite as testdriver when + * using schema supporting databases. + */ + public function canEmulateSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + throw new DBALException('Sqlite platform does not support alter primary key.'); + } + + /** + * {@inheritdoc} + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritdoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + throw new DBALException('Sqlite platform does not support alter constraint.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags = null) + { + $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags; + + return parent::getCreateTableSQL($table, $createFlags); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = str_replace('.', '__', $table); + $table = $this->quoteStringLiteral($table); + + return "PRAGMA foreign_key_list($table)"; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = $this->getSimpleAlterTableSQL($diff); + if (false !== $sql) { + return $sql; + } + + $fromTable = $diff->fromTable; + if ( ! $fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $table = clone $fromTable; + + $columns = array(); + $oldColumnNames = array(); + $newColumnNames = array(); + $columnSql = array(); + + foreach ($table->getColumns() as $columnName => $column) { + $columnName = strtolower($columnName); + $columns[$columnName] = $column; + $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); + } + + foreach ($diff->removedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + unset($oldColumnNames[$columnName]); + unset($newColumnNames[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = strtolower($oldColumnName); + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($column->getName())] = $column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $column->getQuotedName($this); + } + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this); + } + } + + foreach ($diff->addedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columns[strtolower($columnName)] = $column; + } + + $sql = array(); + $tableSql = array(); + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $dataTable = new Table('__temp__'.$table->getName()); + + $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions()); + $newTable->addOption('alter', true); + + $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); + //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0)); + $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($fromTable); + + $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); + $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($dataTable); + + if ($diff->newName && $diff->newName != $diff->name) { + $renamedTable = $diff->getNewName(); + $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this); + } + + $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array|bool + */ + private function getSimpleAlterTableSQL(TableDiff $diff) + { + // Suppress changes on integer type autoincrement columns. + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ( ! $columnDiff->fromColumn instanceof Column || + ! $columnDiff->column instanceof Column || + ! $columnDiff->column->getAutoincrement() || + ! (string) $columnDiff->column->getType() === 'Integer' + ) { + continue; + } + + if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) { + unset($diff->changedColumns[$oldColumnName]); + + continue; + } + + $fromColumnType = (string) $columnDiff->fromColumn->getType(); + + if ($fromColumnType === 'SmallInt' || $fromColumnType === 'BigInt') { + unset($diff->changedColumns[$oldColumnName]); + } + } + + if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes) + || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes) + || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes) + || ! empty($diff->renamedIndexes) + ) { + return false; + } + + $table = new Table($diff->name); + + $sql = array(); + $tableSql = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $field = array_merge(array('unique' => null, 'autoincrement' => null, 'default' => null), $column->toArray()); + $type = (string) $field['type']; + switch (true) { + case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']: + case $type == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL(): + case $type == 'Date' && $field['default'] == $this->getCurrentDateSQL(): + case $type == 'Time' && $field['default'] == $this->getCurrentTimeSQL(): + return false; + } + + $field['name'] = $column->getQuotedName($this); + if (strtolower($field['type']) == 'string' && $field['length'] === null) { + $field['length'] = 255; + } + + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $newTable = new Identifier($diff->newName); + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this); + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getColumnNamesInAlteredTable(TableDiff $diff) + { + $columns = array(); + + foreach ($diff->fromTable->getColumns() as $columnName => $column) { + $columns[strtolower($columnName)] = $column->getName(); + } + + foreach ($diff->removedColumns as $columnName => $column) { + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + $columnName = $column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + $columnName = $columnDiff->column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->addedColumns as $columnName => $column) { + $columns[strtolower($columnName)] = $columnName; + } + + return $columns; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return \Doctrine\DBAL\Schema\Index[] + */ + private function getIndexesInAlteredTable(TableDiff $diff) + { + $indexes = $diff->fromTable->getIndexes(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($indexes as $key => $index) { + foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) { + if (strtolower($key) === strtolower($oldIndexName)) { + unset($indexes[$key]); + } + } + + $changed = false; + $indexColumns = array(); + foreach ($index->getColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($indexes[$key]); + continue 2; + } else { + $indexColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags()); + } + } + + foreach ($diff->removedIndexes as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName) && isset($indexes[$indexName])) { + unset($indexes[$indexName]); + } + } + + foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName)) { + $indexes[$indexName] = $index; + } else { + $indexes[] = $index; + } + } + + return $indexes; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getForeignKeysInAlteredTable(TableDiff $diff) + { + $foreignKeys = $diff->fromTable->getForeignKeys(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($foreignKeys as $key => $constraint) { + $changed = false; + $localColumns = array(); + foreach ($constraint->getLocalColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($foreignKeys[$key]); + continue 2; + } else { + $localColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions()); + } + } + + foreach ($diff->removedForeignKeys as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName) && isset($foreignKeys[$constraintName])) { + unset($foreignKeys[$constraintName]); + } + } + + foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName)) { + $foreignKeys[$constraintName] = $constraint; + } else { + $foreignKeys[] = $constraint; + } + } + + return $foreignKeys; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getPrimaryIndexInAlteredTable(TableDiff $diff) + { + $primaryIndex = array(); + + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + $primaryIndex = array($index->getName() => $index); + } + } + + return $primaryIndex; + } +} diff --git a/wbce/include/Doctrine/DBAL/Portability/Connection.php b/wbce/include/Doctrine/DBAL/Portability/Connection.php new file mode 100644 index 000000000..f6dccdbe1 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,150 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Portability wrapper for a Connection. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Connection extends \Doctrine\DBAL\Connection +{ + const PORTABILITY_ALL = 255; + const PORTABILITY_NONE = 0; + const PORTABILITY_RTRIM = 1; + const PORTABILITY_EMPTY_TO_NULL = 4; + const PORTABILITY_FIX_CASE = 8; + + const PORTABILITY_DB2 = 13; + const PORTABILITY_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + const PORTABILITY_DRIZZLE = 13; + const PORTABILITY_SQLANYWHERE = 13; + const PORTABILITY_SQLSRV = 13; + + /** + * @var integer + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var integer + */ + private $case; + + /** + * {@inheritdoc} + */ + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->getDatabasePlatform()->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } elseif ($this->getDatabasePlatform()->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } elseif ($this->getDatabasePlatform()->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } elseif ($this->getDatabasePlatform()->getName() === "drizzle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_DRIZZLE; + } elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLANYWHERE; + } elseif ($this->getDatabasePlatform()->getName() === 'db2') { + $params['portability'] = $params['portability'] & self::PORTABILITY_DB2; + } elseif ($this->getDatabasePlatform()->getName() === 'mssql') { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLSRV; + } else { + $params['portability'] = $params['portability'] & self::PORTABILITY_OTHERVENDORS; + } + $this->portability = $params['portability']; + } + if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { + if ($this->_conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // make use of c-level support for case handling + $this->_conn->setAttribute(\PDO::ATTR_CASE, $params['fetch_case']); + } else { + $this->case = ($params['fetch_case'] == \PDO::CASE_LOWER) ? CASE_LOWER : CASE_UPPER; + } + } + } + + return $ret; + } + + /** + * @return integer + */ + public function getPortability() + { + return $this->portability; + } + + /** + * @return integer + */ + public function getFetchCase() + { + return $this->case; + } + + /** + * {@inheritdoc} + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + $stmt = new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function prepare($statement) + { + $stmt = new Statement(parent::prepare($statement), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function query() + { + $this->connect(); + + $stmt = $this->_conn->query(...func_get_args()); + $stmt = new Statement($stmt, $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } +} diff --git a/wbce/include/Doctrine/DBAL/Portability/Statement.php b/wbce/include/Doctrine/DBAL/Portability/Statement.php new file mode 100644 index 000000000..15741e372 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability wrapper for a Statement. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + /** + * @var integer + */ + private $portability; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var integer + */ + private $case; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures. + * + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\DBAL\Portability\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + return $this->stmt->bindParam($column, $variable, $type, $length); + } + /** + * {@inheritdoc} + */ + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + { + $this->defaultFetchMode = $fetchMode; + + return $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + $row = $this->stmt->fetch($fetchMode); + + $row = $this->fixRow($row, + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM), + !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE) + ); + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($fetchArgument) { + $rows = $this->stmt->fetchAll($fetchMode, $fetchArgument); + } else { + $rows = $this->stmt->fetchAll($fetchMode); + } + + $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); + $fixCase = !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE); + if ( ! $iterateRow && !$fixCase) { + return $rows; + } + + if ($fetchMode === PDO::FETCH_COLUMN) { + foreach ($rows as $num => $row) { + $rows[$num] = array($row); + } + } + + foreach ($rows as $num => $row) { + $rows[$num] = $this->fixRow($row, $iterateRow, $fixCase); + } + + if ($fetchMode === PDO::FETCH_COLUMN) { + foreach ($rows as $num => $row) { + $rows[$num] = $row[0]; + } + } + + return $rows; + } + + /** + * @param mixed $row + * @param integer $iterateRow + * @param boolean $fixCase + * + * @return array + */ + protected function fixRow($row, $iterateRow, $fixCase) + { + if ( ! $row) { + return $row; + } + + if ($fixCase) { + $row = array_change_key_case($row, $this->case); + } + + if ($iterateRow) { + foreach ($row as $k => $v) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $v === '') { + $row[$k] = null; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $value = $this->stmt->fetchColumn($columnIndex); + + if ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $value === '') { + $value = null; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/wbce/include/Doctrine/DBAL/Query/Expression/CompositeExpression.php new file mode 100644 index 000000000..40dae5fcc --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class CompositeExpression implements \Countable +{ + /** + * Constant that represents an AND composite expression. + */ + const TYPE_AND = 'AND'; + + /** + * Constant that represents an OR composite expression. + */ + const TYPE_OR = 'OR'; + + /** + * The instance type of composite expression. + * + * @var string + */ + private $type; + + /** + * Each expression part of the composite expression. + * + * @var array + */ + private $parts = []; + + /** + * Constructor. + * + * @param string $type Instance type of composite expression. + * @param array $parts Composition of expressions to be joined on composite expression. + */ + public function __construct($type, array $parts = []) + { + $this->type = $type; + + $this->addMultiple($parts); + } + + /** + * Adds multiple parts to composite expression. + * + * @param array $parts + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function addMultiple(array $parts = []) + { + foreach ((array) $parts as $part) { + $this->add($part); + } + + return $this; + } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function add($part) + { + if (empty($part)) { + return $this; + } + + if ($part instanceof self && 0 === count($part)) { + return $this; + } + + $this->parts[] = $part; + + return $this; + } + + /** + * Retrieves the amount of expressions on composite expression. + * + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * Retrieves the string representation of this composite expression. + * + * @return string + */ + public function __toString() + { + if ($this->count() === 1) { + return (string) $this->parts[0]; + } + + return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; + } + + /** + * Returns the type of this composite expression (AND/OR). + * + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/wbce/include/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/wbce/include/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php new file mode 100644 index 000000000..2736fcf22 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,313 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The DBAL Connection. + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) AND (u.role = ?) + * $expr->andX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) OR (u.role = ?) + * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x The left expression. + * @param string $operator One of the ExpressionBuilder::* constants. + * @param mixed $y The right expression. + * + * @return string + */ + public function comparison($x, $operator, $y) + { + return $x . ' ' . $operator . ' ' . $y; + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ? + * $expr->eq('u.id', '?'); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function eq($x, $y) + { + return $this->comparison($x, self::EQ, $y); + } + + /** + * Creates a non equality comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> 1 + * $q->where($q->expr()->neq('u.id', '1')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function neq($x, $y) + { + return $this->comparison($x, self::NEQ, $y); + } + + /** + * Creates a lower-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ? + * $q->where($q->expr()->lt('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function lt($x, $y) + { + return $this->comparison($x, self::LT, $y); + } + + /** + * Creates a lower-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ? + * $q->where($q->expr()->lte('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function lte($x, $y) + { + return $this->comparison($x, self::LTE, $y); + } + + /** + * Creates a greater-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ? + * $q->where($q->expr()->gt('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function gt($x, $y) + { + return $this->comparison($x, self::GT, $y); + } + + /** + * Creates a greater-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ? + * $q->where($q->expr()->gte('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function gte($x, $y) + { + return $this->comparison($x, self::GTE, $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x The field in string format to be restricted by IS NULL. + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x The field in string format to be restricted by IS NOT NULL. + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return string + */ + public function like($x, $y) + { + return $this->comparison($x, 'LIKE', $y); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by NOT LIKE() comparison. + * @param mixed $y Argument to be used in NOT LIKE() comparison. + * + * @return string + */ + public function notLike($x, $y) + { + return $this->comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by IN() comparison. + * + * @return string + */ + public function in($x, $y) + { + return $this->comparison($x, 'IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Creates a NOT IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by NOT IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison. + * + * @return string + */ + public function notIn($x, $y) + { + return $this->comparison($x, 'NOT IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param string|null $type The type of the parameter. + * + * @return string + */ + public function literal($input, $type = null) + { + return $this->connection->quote($input, $type); + } +} diff --git a/wbce/include/Doctrine/DBAL/Query/QueryBuilder.php b/wbce/include/Doctrine/DBAL/Query/QueryBuilder.php new file mode 100644 index 000000000..1b1bc3165 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1356 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression; +use Doctrine\DBAL\Connection; + +/** + * QueryBuilder class is responsible to dynamically create SQL queries. + * + * Important: Verify that every feature you use will work with your database vendor. + * SQL Query Builder does not attempt to validate the generated SQL at all. + * + * The query builder does no validation whatsoever if certain features even work with the + * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements + * even if some vendors such as MySQL support it. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class QueryBuilder +{ + /* + * The query types. + */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + const INSERT = 3; + + /* + * The builder states. + */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * @var array The array of SQL parts collected. + */ + private $sqlParts = [ + 'select' => [], + 'from' => [], + 'join' => [], + 'set' => [], + 'where' => null, + 'groupBy' => [], + 'having' => null, + 'orderBy' => [], + 'values' => [], + ]; + + /** + * The complete SQL string for this query. + * + * @var string + */ + private $sql; + + /** + * The query parameters. + * + * @var array + */ + private $params = []; + + /** + * The parameter type map of this query. + * + * @var array + */ + private $paramTypes = []; + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $state = self::STATE_CLEAN; + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue). + * + * @var integer + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The DBAL Connection. + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function expr() + { + return $this->connection->getExpressionBuilder(); + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Gets the associated DBAL Connection for this query builder. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Executes this query using the bound parameters and their types. + * + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * for insert, update and delete statements. + * + * @return \Doctrine\DBAL\Driver\Statement|int + */ + public function execute() + { + if ($this->type == self::SELECT) { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } else { + return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes); + } + } + + /** + * Gets the complete SQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getSQL(); // SELECT u FROM User u + * + * + * @return string The SQL query string. + */ + public function getSQL() + { + if ($this->sql !== null && $this->state === self::STATE_CLEAN) { + return $this->sql; + } + + switch ($this->type) { + case self::INSERT: + $sql = $this->getSQLForInsert(); + break; + case self::DELETE: + $sql = $this->getSQLForDelete(); + break; + + case self::UPDATE: + $sql = $this->getSQLForUpdate(); + break; + + case self::SELECT: + default: + $sql = $this->getSQLForSelect(); + break; + } + + $this->state = self::STATE_CLEAN; + $this->sql = $sql; + + return $sql; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id') + * ->setParameter(':user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|integer|null $type One of the PDO::PARAM_* constants. + * + * @return $this This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + if ($type !== null) { + $this->paramTypes[$key] = $type; + } + + $this->params[$key] = $value; + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * ':user_id1' => 1, + * ':user_id2' => 2 + * )); + * + * + * @param array $params The query parameters to set. + * @param array $types The query parameters types to set. + * + * @return $this This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = []) + { + $this->paramTypes = $types; + $this->params = $params; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameters indexed by parameter index or name. + */ + public function getParameters() + { + return $this->params; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : null; + } + + /** + * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameter types indexed by parameter index or name. + */ + public function getParameterTypes() + { + return $this->paramTypes; + } + + /** + * Gets a (previously set) query parameter type of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter type. + * + * @return mixed The value of the bound parameter type. + */ + public function getParameterType($key) + { + return isset($this->paramTypes[$key]) ? $this->paramTypes[$key] : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return $this This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->state = self::STATE_DIRTY; + $this->firstResult = $firstResult; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * + * @return $this This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->state = self::STATE_DIRTY; + $this->maxResults = $maxResults; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer The maximum number of results. + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $sqlPartName + * @param string $sqlPart + * @param boolean $append + * + * @return $this This QueryBuilder instance. + */ + public function add($sqlPartName, $sqlPart, $append = false) + { + $isArray = is_array($sqlPart); + $isMultiple = is_array($this->sqlParts[$sqlPartName]); + + if ($isMultiple && !$isArray) { + $sqlPart = array($sqlPart); + } + + $this->state = self::STATE_DIRTY; + + if ($append) { + if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") { + foreach ($sqlPart as $part) { + $this->sqlParts[$sqlPartName][] = $part; + } + } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } elseif ($isMultiple) { + $this->sqlParts[$sqlPartName][] = $sqlPart; + } else { + $this->sqlParts[$sqlPartName] = $sqlPart; + } + + return $this; + } + + $this->sqlParts[$sqlPartName] = $sqlPart; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id', 'p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * + * @return $this This QueryBuilder instance. + */ + public function select($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, false); + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->addSelect('p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expression. + * + * @return $this This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain table. + * + * + * $qb = $conn->createQueryBuilder() + * ->delete('users', 'u') + * ->where('u.id = :user_id'); + * ->setParameter(':user_id', 1); + * + * + * @param string $delete The table whose rows are subject to the deletion. + * @param string $alias The table alias used in the constructed query. + * + * @return $this This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', [ + 'table' => $delete, + 'alias' => $alias + ]); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The table whose rows are subject to the update. + * @param string $alias The table alias used in the constructed query. + * + * @return $this This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', [ + 'table' => $update, + 'alias' => $alias + ]); + } + + /** + * Turns the query being built into an insert query that inserts into + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param string $insert The table into which the rows should be inserted. + * + * @return $this This QueryBuilder instance. + */ + public function insert($insert = null) + { + $this->type = self::INSERT; + + if ( ! $insert) { + return $this; + } + + return $this->add('from', [ + 'table' => $insert + ]); + } + + /** + * Creates and adds a query root corresponding to the table identified by the + * given alias, forming a cartesian product with any existing query roots. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->from('users', 'u') + * + * + * @param string $from The table. + * @param string|null $alias The alias of the table. + * + * @return $this This QueryBuilder instance. + */ + public function from($from, $alias = null) + { + return $this->add('from', [ + 'table' => $from, + 'alias' => $alias + ], true); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return $this This QueryBuilder instance. + */ + public function join($fromAlias, $join, $alias, $condition = null) + { + return $this->innerJoin($fromAlias, $join, $alias, $condition); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return $this This QueryBuilder instance. + */ + public function innerJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', [ + $fromAlias => [ + 'joinType' => 'inner', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ] + ], true); + } + + /** + * Creates and adds a left join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return $this This QueryBuilder instance. + */ + public function leftJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', [ + $fromAlias => [ + 'joinType' => 'left', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ] + ], true); + } + + /** + * Creates and adds a right join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return $this This QueryBuilder instance. + */ + public function rightJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', [ + $fromAlias => [ + 'joinType' => 'right', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ] + ], true); + } + + /** + * Sets a new value for a column in a bulk update query. + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The column to set. + * @param string $value The value, expression, placeholder, etc. + * + * @return $this This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', $key .' = ' . $value, true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $conn->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * + * @return $this This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression)) { + $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * + * @return $this This QueryBuilder instance. + * + * @see where() + */ + public function andWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement. + * + * @return $this This QueryBuilder instance. + * + * @see where() + */ + public function orWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.id'); + * + * + * @param mixed $groupBy The grouping expression. + * + * @return $this This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, false); + } + + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param mixed $groupBy The grouping expression. + * + * @return $this This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, true); + } + + /** + * Sets a value for a column in an insert query. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?' + * ) + * ) + * ->setValue('password', '?'); + * + * + * @param string $column The column into which the value should be inserted. + * @param string $value The value that should be inserted into the column. + * + * @return $this This QueryBuilder instance. + */ + public function setValue($column, $value) + { + $this->sqlParts['values'][$column] = $value; + + return $this; + } + + /** + * Specifies values for an insert query indexed by column names. + * Replaces any previous values, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param array $values The values to specify for the insert query indexed by column names. + * + * @return $this This QueryBuilder instance. + */ + public function values(array $values) + { + return $this->add('values', $values); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * + * @return $this This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) { + $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * + * @return $this This QueryBuilder instance. + */ + public function andHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * + * @return $this This QueryBuilder instance. + */ + public function orHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return $this This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false); + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return $this This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Gets all query parts. + * + * @return array + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Resets SQL parts. + * + * @param array|null $queryPartNames + * + * @return $this This QueryBuilder instance. + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Resets a single SQL part. + * + * @param string $queryPartName + * + * @return $this This QueryBuilder instance. + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Query\QueryException + */ + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']); + + $query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '') + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '') + . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '') + . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '') + . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : ''); + + if ($this->isLimitQuery()) { + return $this->connection->getDatabasePlatform()->modifyLimitQuery( + $query, + $this->maxResults, + $this->firstResult + ); + } + + return $query; + } + + /** + * @return string[] + */ + private function getFromClauses() + { + $fromClauses = array(); + $knownAliases = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + if ($from['alias'] === null) { + $tableSql = $from['table']; + $tableReference = $from['table']; + } else { + $tableSql = $from['table'] . ' ' . $from['alias']; + $tableReference = $from['alias']; + } + + $knownAliases[$tableReference] = true; + + $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases); + } + + $this->verifyAllAliasesAreKnown($knownAliases); + + return $fromClauses; + } + + /** + * @param array $knownAliases + * + * @throws QueryException + */ + private function verifyAllAliasesAreKnown(array $knownAliases) + { + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($knownAliases[$fromAlias])) { + throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + } + } + } + + /** + * @return bool + */ + private function isLimitQuery() + { + return $this->maxResults !== null || $this->firstResult !== null; + } + + /** + * Converts this instance into an INSERT string in SQL. + * + * @return string + */ + private function getSQLForInsert() + { + return 'INSERT INTO ' . $this->sqlParts['from']['table'] . + ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . + ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; + } + + /** + * Converts this instance into an UPDATE string in SQL. + * + * @return string + */ + private function getSQLForUpdate() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'UPDATE ' . $table + . ' SET ' . implode(", ", $this->sqlParts['set']) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Converts this instance into a DELETE string in SQL. + * + * @return string + */ + private function getSQLForDelete() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final SQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getSQL(); + } + + /** + * Creates a new named parameter and bind the value $value to it. + * + * This method provides a shortcut for PDOStatement::bindValue + * when using prepared statements. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided bindValue() will automatically create a + * placeholder for you. An automatic placeholder will be of the name + * ':dcValue1', ':dcValue2' etc. + * + * For more information see {@link http://php.net/pdostatement-bindparam} + * + * Example: + * + * $value = 2; + * $q->eq( 'id', $q->bindValue( $value ) ); + * $stmt = $q->executeQuery(); // executed with 'id = 2' + * + * + * @license New BSD License + * @link http://www.zetacomponents.org + * + * @param mixed $value + * @param mixed $type + * @param string $placeHolder The name to bind with. The string must start with a colon ':'. + * + * @return string the placeholder name used. + */ + public function createNamedParameter($value, $type = \PDO::PARAM_STR, $placeHolder = null) + { + if ($placeHolder === null) { + $this->boundCounter++; + $placeHolder = ":dcValue" . $this->boundCounter; + } + $this->setParameter(substr($placeHolder, 1), $value, $type); + + return $placeHolder; + } + + /** + * Creates a new positional parameter and bind the given value to it. + * + * Attention: If you are using positional parameters with the query builder you have + * to be very careful to bind all parameters in the order they appear in the SQL + * statement , otherwise they get bound in the wrong order which can lead to serious + * bugs in your code. + * + * Example: + * + * $qb = $conn->createQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR)) + * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR)) + * + * + * @param mixed $value + * @param integer $type + * + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + + return "?"; + } + + /** + * @param string $fromAlias + * @param array $knownAliases + * + * @return string + * + * @throws QueryException + */ + private function getSQLForJoins($fromAlias, array &$knownAliases) + { + $sql = ''; + + if (isset($this->sqlParts['join'][$fromAlias])) { + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + if (array_key_exists($join['joinAlias'], $knownAliases)) { + throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases)); + } + $sql .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + $knownAliases[$join['joinAlias']] = true; + } + + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); + } + } + + return $sql; + } + + /** + * Deep clone of all expression objects in the SQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->sqlParts as $part => $elements) { + if (is_array($this->sqlParts[$part])) { + foreach ($this->sqlParts[$part] as $idx => $element) { + if (is_object($element)) { + $this->sqlParts[$part][$idx] = clone $element; + } + } + } elseif (is_object($elements)) { + $this->sqlParts[$part] = clone $elements; + } + } + + foreach ($this->params as $name => $param) { + if (is_object($param)) { + $this->params[$name] = clone $param; + } + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Query/QueryException.php b/wbce/include/Doctrine/DBAL/Query/QueryException.php new file mode 100644 index 000000000..92c9336d6 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function unknownAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not part of " . + "any FROM or JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } + + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function nonUniqueAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not unique " . + "in FROM and JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } +} diff --git a/wbce/include/Doctrine/DBAL/README.markdown b/wbce/include/Doctrine/DBAL/README.markdown new file mode 100644 index 000000000..e69de29bb diff --git a/wbce/include/Doctrine/DBAL/SQLParserUtils.php b/wbce/include/Doctrine/DBAL/SQLParserUtils.php new file mode 100644 index 000000000..4242989cb --- /dev/null +++ b/wbce/include/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLParserUtils +{ + const POSITIONAL_TOKEN = '\?'; + const NAMED_TOKEN = '(? integer pair (indexed from zero) for a positional statement + * and a string => int[] pair for a named statement. + * + * @param string $statement + * @param boolean $isPositional + * + * @return array + */ + static public function getPlaceholderPositions($statement, $isPositional = true) + { + $match = ($isPositional) ? '?' : ':'; + if (strpos($statement, $match) === false) { + return array(); + } + + $token = ($isPositional) ? self::POSITIONAL_TOKEN : self::NAMED_TOKEN; + $paramMap = array(); + + foreach (self::getUnquotedStatementFragments($statement) as $fragment) { + preg_match_all("/$token/", $fragment[0], $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $placeholder) { + if ($isPositional) { + $paramMap[] = $placeholder[1] + $fragment[1]; + } else { + $pos = $placeholder[1] + $fragment[1]; + $paramMap[$pos] = substr($placeholder[0], 1, strlen($placeholder[0])); + } + } + } + + return $paramMap; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query. + * @param array $types The types the previous parameters are in. + * + * @return array + * + * @throws SQLParserUtilsException + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + + if ($isPositional) { + ksort($params); + ksort($types); + } + + foreach ($types as $name => $type) { + ++$bindIndex; + + if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + continue; + } + + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + + if (( ! $arrayPositions && $isPositional)) { + return array($query, $params, $types); + } + + $paramPos = self::getPlaceholderPositions($query, $isPositional); + + if ($isPositional) { + $paramOffset = 0; + $queryOffset = 0; + $params = array_values($params); + $types = array_values($types); + + foreach ($paramPos as $needle => $needlePos) { + if ( ! isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $count = count($params[$needle]); + + $params = array_merge( + array_slice($params, 0, $needle), + $params[$needle], + array_slice($params, $needle + 1) + ); + + $types = array_merge( + array_slice($types, 0, $needle), + $count ? + array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100 + array(), + array_slice($types, $needle + 1) + ); + + $expandStr = $count ? implode(", ", array_fill(0, $count, "?")) : 'NULL'; + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle. + $queryOffset += (strlen($expandStr) - 1); + } + + return array($query, $params, $types); + } + + $queryOffset = 0; + $typesOrd = array(); + $paramsOrd = array(); + + foreach ($paramPos as $pos => $paramName) { + $paramLen = strlen($paramName) + 1; + $value = static::extractParam($paramName, $params, true); + + if ( ! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) { + $pos += $queryOffset; + $queryOffset -= ($paramLen - 1); + $paramsOrd[] = $value; + $typesOrd[] = static::extractParam($paramName, $types, false, \PDO::PARAM_STR); + $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); + + continue; + } + + $count = count($value); + $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + + foreach ($value as $val) { + $paramsOrd[] = $val; + $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET; + } + + $pos += $queryOffset; + $queryOffset += (strlen($expandStr) - $paramLen); + $query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen)); + } + + return array($query, $paramsOrd, $typesOrd); + } + + /** + * Slice the SQL statement around pairs of quotes and + * return string fragments of SQL outside of quoted literals. + * Each fragment is captured as a 2-element array: + * + * 0 => matched fragment string, + * 1 => offset of fragment in $statement + * + * @param string $statement + * @return array + */ + static private function getUnquotedStatementFragments($statement) + { + $literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' . + self::ESCAPED_DOUBLE_QUOTED_TEXT . '|' . + self::ESCAPED_BACKTICK_QUOTED_TEXT . '|' . + self::ESCAPED_BRACKET_QUOTED_TEXT; + preg_match_all("/([^'\"`\[]+)(?:$literal)?/s", $statement, $fragments, PREG_OFFSET_CAPTURE); + + return $fragments[1]; + } + + /** + * @param string $paramName The name of the parameter (without a colon in front) + * @param array $paramsOrTypes A hash of parameters or types + * @param bool $isParam + * @param mixed $defaultValue An optional default value. If omitted, an exception is thrown + * + * @throws SQLParserUtilsException + * @return mixed + */ + static private function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null) + { + if (array_key_exists($paramName, $paramsOrTypes)) { + return $paramsOrTypes[$paramName]; + } + + // Hash keys can be prefixed with a colon for compatibility + if (array_key_exists(':' . $paramName, $paramsOrTypes)) { + return $paramsOrTypes[':' . $paramName]; + } + + if (null !== $defaultValue) { + return $defaultValue; + } + + if ($isParam) { + throw SQLParserUtilsException::missingParam($paramName); + } + + throw SQLParserUtilsException::missingType($paramName); + } +} diff --git a/wbce/include/Doctrine/DBAL/SQLParserUtilsException.php b/wbce/include/Doctrine/DBAL/SQLParserUtilsException.php new file mode 100644 index 000000000..25c7209c7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/SQLParserUtilsException.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +class SQLParserUtilsException extends DBALException +{ + /** + * @param string $paramName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingParam($paramName) + { + return new self(sprintf('Value for :%1$s not found in params array. Params array key should be "%1$s"', $paramName)); + } + + /** + * @param string $typeName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingType($typeName) + { + return new self(sprintf('Value for :%1$s not found in types array. Types array key should be "%1$s"', $typeName)); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/AbstractAsset.php b/wbce/include/Doctrine/DBAL/Schema/AbstractAsset.php new file mode 100644 index 000000000..051dce92c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,227 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * The abstract asset allows to reset the name of all assets without publishing this to the public userland. + * + * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables + * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class AbstractAsset +{ + /** + * @var string + */ + protected $_name; + + /** + * Namespace of the asset. If none isset the default namespace is assumed. + * + * @var string|null + */ + protected $_namespace = null; + + /** + * @var boolean + */ + protected $_quoted = false; + + /** + * Sets the name of this asset. + * + * @param string $name + * + * @return void + */ + protected function _setName($name) + { + if ($this->isIdentifierQuoted($name)) { + $this->_quoted = true; + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") !== false) { + $parts = explode(".", $name); + $this->_namespace = $parts[0]; + $name = $parts[1]; + } + $this->_name = $name; + } + + /** + * Is this asset in the default namespace? + * + * @param string $defaultNamespaceName + * + * @return boolean + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Gets the namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string|null + */ + public function getNamespaceName() + { + return $this->_namespace; + } + + /** + * The shortest name is stripped of the default namespace. All other + * namespaced elements are returned as full-qualified names. + * + * @param string $defaultNamespaceName + * + * @return string + */ + public function getShortestName($defaultNamespaceName) + { + $shortestName = $this->getName(); + if ($this->_namespace == $defaultNamespaceName) { + $shortestName = $this->_name; + } + + return strtolower($shortestName); + } + + /** + * The normalized name is full-qualified and lowerspaced. Lowerspacing is + * actually wrong, but we have to do it to keep our sanity. If you are + * using database objects that only differentiate in the casing (FOO vs + * Foo) then you will NOT be able to use Doctrine Schema abstraction. + * + * Every non-namespaced element is prefixed with the default namespace + * name which is passed as argument to this method. + * + * @param string $defaultNamespaceName + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if ( ! $this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + + return strtolower($name); + } + + /** + * Checks if this asset's name is quoted. + * + * @return boolean + */ + public function isQuoted() + { + return $this->_quoted; + } + + /** + * Checks if this identifier is quoted. + * + * @param string $identifier + * + * @return boolean + */ + protected function isIdentifierQuoted($identifier) + { + return (isset($identifier[0]) && ($identifier[0] == '`' || $identifier[0] == '"' || $identifier[0] == '[')); + } + + /** + * Trim quotes from the identifier. + * + * @param string $identifier + * + * @return string + */ + protected function trimQuotes($identifier) + { + return str_replace(array('`', '"', '[', ']'), '', $identifier); + } + + /** + * Returns the name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + + return $this->_name; + } + + /** + * Gets the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getName()); + foreach ($parts as $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * Generates an identifier from a list of column names obeying a certain string length. + * + * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, + * however building idents automatically for foreign keys, composite keys or such can easily create + * very long names. + * + * @param array $columnNames + * @param string $prefix + * @param integer $maxSize + * + * @return string + */ + protected function _generateIdentifierName($columnNames, $prefix='', $maxSize=30) + { + $hash = implode("", array_map(function ($column) { + return dechex(crc32($column)); + }, $columnNames)); + + return substr(strtoupper($prefix . "_" . $hash), 0, $maxSize); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/AbstractSchemaManager.php new file mode 100644 index 000000000..ab3b9d68f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,1113 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Base class for schema managers. Schema managers are used to inspect and/or + * modify the database schema/structure. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractSchemaManager +{ + /** + * Holds instance of the Doctrine connection for this schema manager. + * + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * Holds instance of the database platform used for this schema manager. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * Constructor. Accepts the Connection instance to manage the schema for. + * + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\DBAL\Platforms\AbstractPlatform|null $platform + */ + public function __construct(\Doctrine\DBAL\Connection $conn, AbstractPlatform $platform = null) + { + $this->_conn = $conn; + $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + } + + /** + * Returns the associated platform. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Tries any method on the schema manager. Normally a method throws an + * exception when your DBMS doesn't support it or if an error occurs. + * This method allows you to try and method on your SchemaManager + * instance and will return false if it does not work or is not supported. + * + * + * $result = $sm->tryMethod('dropView', 'view_name'); + * + * + * @return mixed + */ + public function tryMethod() + { + $args = func_get_args(); + $method = $args[0]; + unset($args[0]); + $args = array_values($args); + + try { + return call_user_func_array(array($this, $method), $args); + } catch (\Exception $e) { + return false; + } + } + + /** + * Lists the available databases for this connection. + * + * @return array + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * Returns a list of all namespaces in the current database. + * + * @return array + */ + public function listNamespaceNames() + { + $sql = $this->_platform->getListNamespacesSQL(); + + $namespaces = $this->_conn->fetchAll($sql); + + return $this->getPortableNamespacesList($namespaces); + } + + /** + * Lists the available sequences for this connection. + * + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\Sequence[] + */ + public function listSequences($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListSequencesSQL($database); + + $sequences = $this->_conn->fetchAll($sql); + + return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); + } + + /** + * Lists the columns for a given table. + * + * In contrast to other libraries and to the old version of Doctrine, + * this column definition does try to contain the 'primary' field for + * the reason that it is not portable across different RDBMS. Use + * {@see listTableIndexes($tableName)} to retrieve the primary key + * of a table. We're a RDBMS specifies more details these are held + * in the platformDetails array. + * + * @param string $table The name of the table. + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\Column[] + */ + public function listTableColumns($table, $database = null) + { + if ( ! $database) { + $database = $this->_conn->getDatabase(); + } + + $sql = $this->_platform->getListTableColumnsSQL($table, $database); + + $tableColumns = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableColumnList($table, $database, $tableColumns); + } + + /** + * Lists the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table. + * + * @return \Doctrine\DBAL\Schema\Index[] + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Returns true if all the given tables exist. + * + * @param array $tableNames + * + * @return boolean + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array) $tableNames); + + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Returns a list of all tables in the current database. + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + + $tables = $this->_conn->fetchAll($sql); + $tableNames = $this->_getPortableTablesList($tables); + + return $this->filterAssetNames($tableNames); + } + + /** + * Filters asset names if they are configured to return only a subset of all + * the found elements. + * + * @param array $assetNames + * + * @return array + */ + protected function filterAssetNames($assetNames) + { + $filterExpr = $this->getFilterSchemaAssetsExpression(); + if ( ! $filterExpr) { + return $assetNames; + } + + return array_values( + array_filter($assetNames, function ($assetName) use ($filterExpr) { + $assetName = ($assetName instanceof AbstractAsset) ? $assetName->getName() : $assetName; + + return preg_match($filterExpr, $assetName); + }) + ); + } + + /** + * @return string|null + */ + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * Lists the tables for this connection. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames as $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + */ + public function listTableDetails($tableName) + { + $columns = $this->listTableColumns($tableName); + $foreignKeys = array(); + if ($this->_platform->supportsForeignKeyConstraints()) { + $foreignKeys = $this->listTableForeignKeys($tableName); + } + $indexes = $this->listTableIndexes($tableName); + + return new Table($tableName, $columns, $indexes, $foreignKeys, false, array()); + } + + /** + * Lists the views this connection has. + * + * @return \Doctrine\DBAL\Schema\View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * Lists the foreign keys for the given table. + * + * @param string $table The name of the table. + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public function listTableForeignKeys($table, $database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /* drop*() Methods */ + + /** + * Drops a database. + * + * NOTE: You can not drop the database this SchemaManager is currently connected to. + * + * @param string $database The name of the database to drop. + * + * @return void + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drops the given table. + * + * @param string $tableName The name of the table to drop. + * + * @return void + */ + public function dropTable($tableName) + { + $this->_execSql($this->_platform->getDropTableSQL($tableName)); + } + + /** + * Drops the index from the given table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index The name of the index. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropIndex($index, $table) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drops the constraint from the given table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey The name of the foreign key. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table with the foreign key. + * + * @return void + */ + public function dropForeignKey($foreignKey, $table) + { + $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); + } + + /** + * Drops a sequence with a given name. + * + * @param string $name The name of the sequence to drop. + * + * @return void + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drops a view. + * + * @param string $name The name of the view. + * + * @return void + */ + public function dropView($name) + { + $this->_execSql($this->_platform->getDropViewSQL($name)); + } + + /* create*() Methods */ + + /** + * Creates a new database. + * + * @param string $database The name of the database to create. + * + * @return void + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Creates a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If something fails at database level. + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Creates a constraint on a table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The ForeignKey instance. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drops and creates a constraint. + * + * @see dropConstraint() + * @see createConstraint() + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drops and creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drops and creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drops and create a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If something fails at database level. + */ + public function dropAndCreateSequence(Sequence $sequence) + { + $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); + $this->createSequence($sequence); + } + + /** + * Drops and creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drops and creates a new database. + * + * @param string $database The name of the database to create. + * + * @return void + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drops and creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alters an existing tables schema. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * + * @return void + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries as $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Renames a given table to another name. + * + * @param string $name The current name of the table. + * @param string $newName The new name of the table. + * + * @return void + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * Methods for filtering return values of list*() methods to convert + * the native DBMS data definition to a portable Doctrine definition + */ + + /** + * @param array $databases + * + * @return array + */ + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespaces The list of namespace names in the native DBMS data definition. + * + * @return array + */ + protected function getPortableNamespacesList(array $namespaces) + { + $namespacesList = array(); + + foreach ($namespaces as $namespace) { + $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); + } + + return $namespacesList; + } + + /** + * @param array $database + * + * @return mixed + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + /** + * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespace The native DBMS namespace definition. + * + * @return mixed + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace; + } + + /** + * @param array $functions + * + * @return array + */ + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $function + * + * @return mixed + */ + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + /** + * @param array $triggers + * + * @return array + */ + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $trigger + * + * @return mixed + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + /** + * @param array $sequences + * + * @return array + */ + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $sequence + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function _getPortableSequenceDefinition($sequence) + { + throw DBALException::notSupported('Sequences'); + } + + /** + * Independent of the database the keys of the column list result are lowercased. + * + * The name of the created column instance however is kept in its case. + * + * @param string $table The name of the table. + * @param string $database + * @param array $tableColumns + * + * @return array + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $eventManager = $this->_platform->getEventManager(); + + $list = array(); + foreach ($tableColumns as $tableColumn) { + $column = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { + $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $column = $eventArgs->getColumn(); + } + + if ( ! $defaultPrevented) { + $column = $this->_getPortableTableColumnDefinition($tableColumn); + } + + if ($column) { + $name = strtolower($column->getQuotedName($this->_platform)); + $list[$name] = $column; + } + } + + return $list; + } + + /** + * Gets Table Column Definition. + * + * @param array $tableColumn + * + * @return \Doctrine\DBAL\Schema\Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregates and groups the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string|null $tableName + * + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + $result = array(); + foreach ($tableIndexRows as $tableIndex) { + $indexName = $keyName = $tableIndex['key_name']; + if ($tableIndex['primary']) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + if (!isset($result[$keyName])) { + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => array($tableIndex['column_name']), + 'unique' => $tableIndex['non_unique'] ? false : true, + 'primary' => $tableIndex['primary'], + 'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(), + 'options' => isset($tableIndex['where']) ? array('where' => $tableIndex['where']) : array(), + ); + } else { + $result[$keyName]['columns'][] = $tableIndex['column_name']; + } + } + + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach ($result as $indexKey => $data) { + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if ( ! $defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + /** + * @param array $tables + * + * @return array + */ + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $table + * + * @return array + */ + protected function _getPortableTableDefinition($table) + { + return $table; + } + + /** + * @param array $users + * + * @return array + */ + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $user + * + * @return mixed + */ + protected function _getPortableUserDefinition($user) + { + return $user; + } + + /** + * @param array $views + * + * @return array + */ + protected function _getPortableViewsList($views) + { + $list = array(); + foreach ($views as $value) { + if ($view = $this->_getPortableViewDefinition($value)) { + $viewName = strtolower($view->getQuotedName($this->_platform)); + $list[$viewName] = $view; + } + } + + return $list; + } + + /** + * @param array $view + * + * @return mixed + */ + protected function _getPortableViewDefinition($view) + { + return false; + } + + /** + * @param array $tableForeignKeys + * + * @return array + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $tableForeignKey + * + * @return mixed + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + /** + * @param array|string $sql + * + * @return void + */ + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Creates a schema instance for the current database. + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function createSchema() + { + $namespaces = array(); + + if ($this->_platform->supportsSchemas()) { + $namespaces = $this->listNamespaceNames(); + } + + $sequences = array(); + + if ($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces); + } + + /** + * Creates the configuration for this schema. + * + * @return \Doctrine\DBAL\Schema\SchemaConfig + */ + public function createSchemaConfig() + { + $schemaConfig = new SchemaConfig(); + $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + + $searchPaths = $this->getSchemaSearchPaths(); + if (isset($searchPaths[0])) { + $schemaConfig->setName($searchPaths[0]); + } + + $params = $this->_conn->getParams(); + if (isset($params['defaultTableOptions'])) { + $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); + } + + return $schemaConfig; + } + + /** + * The search path for namespaces in the currently connected database. + * + * The first entry is usually the default namespace in the Schema. All + * further namespaces contain tables/sequences which can also be addressed + * with a short, not full-qualified name. + * + * For databases that don't support subschema/namespaces this method + * returns the name of the currently connected database. + * + * @return array + */ + public function getSchemaSearchPaths() + { + return array($this->_conn->getDatabase()); + } + + /** + * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns + * the type given as default. + * + * @param string $comment + * @param string $currentType + * + * @return string + */ + public function extractDoctrineTypeFromComment($comment, $currentType) + { + if (preg_match("(\(DC2Type:(((?!\)).)+)\))", $comment, $match)) { + $currentType = $match[1]; + } + + return $currentType; + } + + /** + * @param string $comment + * @param string $type + * + * @return string + */ + public function removeDoctrineTypeFromComment($comment, $type) + { + return str_replace('(DC2Type:'.$type.')', '', $comment); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Column.php b/wbce/include/Doctrine/DBAL/Schema/Column.php new file mode 100644 index 000000000..192ce1d2e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,487 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Object representation of a database column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var Type + */ + protected $_type; + + /** + * @var integer|null + */ + protected $_length = null; + + /** + * @var integer + */ + protected $_precision = 10; + + /** + * @var integer + */ + protected $_scale = 0; + + /** + * @var boolean + */ + protected $_unsigned = false; + + /** + * @var boolean + */ + protected $_fixed = false; + + /** + * @var boolean + */ + protected $_notnull = true; + + /** + * @var string|null + */ + protected $_default = null; + + /** + * @var boolean + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string|null + */ + protected $_columnDefinition = null; + + /** + * @var string|null + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Creates a new Column. + * + * @param string $columnName + * @param Type $type + * @param array $options + */ + public function __construct($columnName, Type $type, array $options=array()) + { + $this->_setName($columnName); + $this->setType($type); + $this->setOptions($options); + } + + /** + * @param array $options + * + * @return Column + */ + public function setOptions(array $options) + { + foreach ($options as $name => $value) { + $method = "set".$name; + if (method_exists($this, $method)) { + $this->$method($value); + } + } + + return $this; + } + + /** + * @param Type $type + * + * @return Column + */ + public function setType(Type $type) + { + $this->_type = $type; + + return $this; + } + + /** + * @param integer|null $length + * + * @return Column + */ + public function setLength($length) + { + if ($length !== null) { + $this->_length = (int) $length; + } else { + $this->_length = null; + } + + return $this; + } + + /** + * @param integer $precision + * + * @return Column + */ + public function setPrecision($precision) + { + if (!is_numeric($precision)) { + $precision = 10; // defaults to 10 when no valid precision is given. + } + + $this->_precision = (int) $precision; + + return $this; + } + + /** + * @param integer $scale + * + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int) $scale; + + return $this; + } + + /** + * @param boolean $unsigned + * + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool) $unsigned; + + return $this; + } + + /** + * @param boolean $fixed + * + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool) $fixed; + + return $this; + } + + /** + * @param boolean $notnull + * + * @return Column + */ + public function setNotnull($notnull) + { + $this->_notnull = (bool) $notnull; + + return $this; + } + + /** + * @param mixed $default + * + * @return Column + */ + public function setDefault($default) + { + $this->_default = $default; + + return $this; + } + + /** + * @param array $platformOptions + * + * @return Column + */ + public function setPlatformOptions(array $platformOptions) + { + $this->_platformOptions = $platformOptions; + + return $this; + } + + /** + * @param string $name + * @param mixed $value + * + * @return Column + */ + public function setPlatformOption($name, $value) + { + $this->_platformOptions[$name] = $value; + + return $this; + } + + /** + * @param string $value + * + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + + return $this; + } + + /** + * @return Type + */ + public function getType() + { + return $this->_type; + } + + /** + * @return integer|null + */ + public function getLength() + { + return $this->_length; + } + + /** + * @return integer + */ + public function getPrecision() + { + return $this->_precision; + } + + /** + * @return integer + */ + public function getScale() + { + return $this->_scale; + } + + /** + * @return boolean + */ + public function getUnsigned() + { + return $this->_unsigned; + } + + /** + * @return boolean + */ + public function getFixed() + { + return $this->_fixed; + } + + /** + * @return boolean + */ + public function getNotnull() + { + return $this->_notnull; + } + + /** + * @return string|null + */ + public function getDefault() + { + return $this->_default; + } + + /** + * @return array + */ + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + /** + * @return string|null + */ + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + /** + * @return boolean + */ + public function getAutoincrement() + { + return $this->_autoincrement; + } + + /** + * @param boolean $flag + * + * @return Column + */ + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + + return $this; + } + + /** + * @param string $comment + * + * @return Column + */ + public function setComment($comment) + { + $this->_comment = $comment; + + return $this; + } + + /** + * @return string|null + */ + public function getComment() + { + return $this->_comment; + } + + /** + * @param string $name + * @param mixed $value + * + * @return Column + */ + public function setCustomSchemaOption($name, $value) + { + $this->_customSchemaOptions[$name] = $value; + + return $this; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasCustomSchemaOption($name) + { + return isset($this->_customSchemaOptions[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getCustomSchemaOption($name) + { + return $this->_customSchemaOptions[$name]; + } + + /** + * @param array $customSchemaOptions + * + * @return Column + */ + public function setCustomSchemaOptions(array $customSchemaOptions) + { + $this->_customSchemaOptions = $customSchemaOptions; + + return $this; + } + + /** + * @return array + */ + public function getCustomSchemaOptions() + { + return $this->_customSchemaOptions; + } + + /** + * @return array + */ + public function toArray() + { + return array_merge(array( + 'name' => $this->_name, + 'type' => $this->_type, + 'default' => $this->_default, + 'notnull' => $this->_notnull, + 'length' => $this->_length, + 'precision' => $this->_precision, + 'scale' => $this->_scale, + 'fixed' => $this->_fixed, + 'unsigned' => $this->_unsigned, + 'autoincrement' => $this->_autoincrement, + 'columnDefinition' => $this->_columnDefinition, + 'comment' => $this->_comment, + ), $this->_platformOptions, $this->_customSchemaOptions); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/ColumnDiff.php b/wbce/include/Doctrine/DBAL/Schema/ColumnDiff.php new file mode 100644 index 000000000..61d7f7319 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represents the change of a column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + /** + * @var string + */ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + /** + * @var Column + */ + public $fromColumn; + + /** + * @param string $oldColumnName + * @param Column $column + * @param string[] $changedProperties + * @param Column $fromColumn + */ + public function __construct($oldColumnName, Column $column, array $changedProperties = array(), Column $fromColumn = null) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + $this->fromColumn = $fromColumn; + } + + /** + * @param string $propertyName + * + * @return boolean + */ + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } + + /** + * @return Identifier + */ + public function getOldColumnName() + { + $quote = $this->fromColumn && $this->fromColumn->isQuoted(); + + return new Identifier($this->oldColumnName, $quote); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Comparator.php b/wbce/include/Doctrine/DBAL/Schema/Comparator.php new file mode 100644 index 000000000..107d1beb5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,541 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types; + +/** + * Compares two Schemas and return an instance of SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + static public function compareSchemas(Schema $fromSchema, Schema $toSchema) + { + $c = new self(); + + return $c->compare($fromSchema, $toSchema); + } + + /** + * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. + * + * The returned differences are returned in such a way that they contain the + * operations to change the schema stored in $fromSchema to the schema that is + * stored in $toSchema. + * + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + $diff->fromSchema = $fromSchema; + + $foreignKeysToTable = array(); + + foreach ($toSchema->getNamespaces() as $namespace) { + if ( ! $fromSchema->hasNamespace($namespace)) { + $diff->newNamespaces[$namespace] = $namespace; + } + } + + foreach ($fromSchema->getNamespaces() as $namespace) { + if ( ! $toSchema->hasNamespace($namespace)) { + $diff->removedNamespaces[$namespace] = $namespace; + } + } + + foreach ($toSchema->getTables() as $table) { + $tableName = $table->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasTable($tableName)) { + $diff->newTables[$tableName] = $toSchema->getTable($tableName); + } else { + $tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName)); + if ($tableDifferences !== false) { + $diff->changedTables[$tableName] = $tableDifferences; + } + } + } + + /* Check if there are tables removed */ + foreach ($fromSchema->getTables() as $table) { + $tableName = $table->getShortestName($fromSchema->getName()); + + $table = $fromSchema->getTable($tableName); + if ( ! $toSchema->hasTable($tableName)) { + $diff->removedTables[$tableName] = $table; + } + + // also remember all foreign keys that point to a specific table + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignTable = strtolower($foreignKey->getForeignTableName()); + if (!isset($foreignKeysToTable[$foreignTable])) { + $foreignKeysToTable[$foreignTable] = array(); + } + $foreignKeysToTable[$foreignTable][] = $foreignKey; + } + } + + foreach ($diff->removedTables as $tableName => $table) { + if (isset($foreignKeysToTable[$tableName])) { + $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); + + // deleting duplicated foreign keys present on both on the orphanedForeignKey + // and the removedForeignKeys from changedTables + foreach ($foreignKeysToTable[$tableName] as $foreignKey) { + // strtolower the table name to make if compatible with getShortestName + $localTableName = strtolower($foreignKey->getLocalTableName()); + if (isset($diff->changedTables[$localTableName])) { + foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { + // We check if the key is from the removed table if not we skip. + if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) { + continue; + } + unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); + } + } + } + } + } + + foreach ($toSchema->getSequences() as $sequence) { + $sequenceName = $sequence->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasSequence($sequenceName)) { + if ( ! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { + $diff->newSequences[] = $sequence; + } + } else { + if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { + $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + } + } + } + + foreach ($fromSchema->getSequences() as $sequence) { + if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { + continue; + } + + $sequenceName = $sequence->getShortestName($fromSchema->getName()); + + if ( ! $toSchema->hasSequence($sequenceName)) { + $diff->removedSequences[] = $sequence; + } + } + + return $diff; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return boolean + */ + private function isAutoIncrementSequenceInSchema($schema, $sequence) + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence1 + * @param \Doctrine\DBAL\Schema\Sequence $sequence2 + * + * @return boolean + */ + public function diffSequence(Sequence $sequence1, Sequence $sequence2) + { + if ($sequence1->getAllocationSize() != $sequence2->getAllocationSize()) { + return true; + } + + if ($sequence1->getInitialValue() != $sequence2->getInitialValue()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the tables $table1 and $table2. + * + * If there are no differences this method returns the boolean false. + * + * @param \Doctrine\DBAL\Schema\Table $table1 + * @param \Doctrine\DBAL\Schema\Table $table2 + * + * @return boolean|\Doctrine\DBAL\Schema\TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + $tableDifferences->fromTable = $table1; + + $table1Columns = $table1->getColumns(); + $table2Columns = $table2->getColumns(); + + /* See if all the fields in table 1 exist in table 2 */ + foreach ($table2Columns as $columnName => $column) { + if ( !$table1->hasColumn($columnName)) { + $tableDifferences->addedColumns[$columnName] = $column; + $changes++; + } + } + /* See if there are any removed fields in table 2 */ + foreach ($table1Columns as $columnName => $column) { + // See if column is removed in table 2. + if ( ! $table2->hasColumn($columnName)) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + continue; + } + + // See if column has changed properties in table 2. + $changedProperties = $this->diffColumn($column, $table2->getColumn($columnName)); + + if ( ! empty($changedProperties)) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $columnDiff->fromColumn = $column; + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + /* See if all the indexes in table 1 exist in table 2 */ + foreach ($table2Indexes as $indexName => $index) { + if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) { + continue; + } + + $tableDifferences->addedIndexes[$indexName] = $index; + $changes++; + } + /* See if there are any removed indexes in table 2 */ + foreach ($table1Indexes as $indexName => $index) { + // See if index is removed in table 2. + if (($index->isPrimary() && ! $table2->hasPrimaryKey()) || + ! $index->isPrimary() && ! $table2->hasIndex($indexName) + ) { + $tableDifferences->removedIndexes[$indexName] = $index; + $changes++; + continue; + } + + // See if index has changed in table 2. + $table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName); + + if ($this->diffIndex($index, $table2Index)) { + $tableDifferences->changedIndexes[$indexName] = $table2Index; + $changes++; + } + } + + $this->detectIndexRenamings($tableDifferences); + + $fromFkeys = $table1->getForeignKeys(); + $toFkeys = $table2->getForeignKeys(); + + foreach ($fromFkeys as $key1 => $constraint1) { + foreach ($toFkeys as $key2 => $constraint2) { + if ($this->diffForeignKey($constraint1, $constraint2) === false) { + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } else { + if (strtolower($constraint1->getName()) == strtolower($constraint2->getName())) { + $tableDifferences->changedForeignKeys[] = $constraint2; + $changes++; + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } + } + } + } + + foreach ($fromFkeys as $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys as $constraint2) { + $tableDifferences->addedForeignKeys[] = $constraint2; + $changes++; + } + + return $changes ? $tableDifferences : false; + } + + /** + * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns as $removedColumn) { + if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { + $renameCandidates[$addedColumn->getName()][] = array($removedColumn, $addedColumn, $addedColumnName); + } + } + } + + foreach ($renameCandidates as $candidateColumns) { + if (count($candidateColumns) == 1) { + list($removedColumn, $addedColumn) = $candidateColumns[0]; + $removedColumnName = strtolower($removedColumn->getName()); + $addedColumnName = strtolower($addedColumn->getName()); + + if ( ! isset($tableDifferences->renamedColumns[$removedColumnName])) { + $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + unset($tableDifferences->addedColumns[$addedColumnName]); + unset($tableDifferences->removedColumns[$removedColumnName]); + } + } + } + } + + /** + * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectIndexRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + + // Gather possible rename candidates by comparing each added and removed index based on semantics. + foreach ($tableDifferences->addedIndexes as $addedIndexName => $addedIndex) { + foreach ($tableDifferences->removedIndexes as $removedIndex) { + if (! $this->diffIndex($addedIndex, $removedIndex)) { + $renameCandidates[$addedIndex->getName()][] = array($removedIndex, $addedIndex, $addedIndexName); + } + } + } + + foreach ($renameCandidates as $candidateIndexes) { + // If the current rename candidate contains exactly one semantically equal index, + // we can safely rename it. + // Otherwise it is unclear if a rename action is really intended, + // therefore we let those ambiguous indexes be added/dropped. + if (count($candidateIndexes) === 1) { + list($removedIndex, $addedIndex) = $candidateIndexes[0]; + + $removedIndexName = strtolower($removedIndex->getName()); + $addedIndexName = strtolower($addedIndex->getName()); + + if (! isset($tableDifferences->renamedIndexes[$removedIndexName])) { + $tableDifferences->renamedIndexes[$removedIndexName] = $addedIndex; + unset($tableDifferences->addedIndexes[$addedIndexName]); + unset($tableDifferences->removedIndexes[$removedIndexName]); + } + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key1 + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key2 + * + * @return boolean + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getUnquotedLocalColumns()) != array_map('strtolower', $key2->getUnquotedLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getUnquotedForeignColumns()) != array_map('strtolower', $key2->getUnquotedForeignColumns())) { + return true; + } + + if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { + return true; + } + + if ($key1->onUpdate() != $key2->onUpdate()) { + return true; + } + + if ($key1->onDelete() != $key2->onDelete()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the fields $field1 and $field2. + * + * If there are differences this method returns $field2, otherwise the + * boolean false. + * + * @param \Doctrine\DBAL\Schema\Column $column1 + * @param \Doctrine\DBAL\Schema\Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $properties1 = $column1->toArray(); + $properties2 = $column2->toArray(); + + $changedProperties = array(); + + foreach (array('type', 'notnull', 'unsigned', 'autoincrement') as $property) { + if ($properties1[$property] != $properties2[$property]) { + $changedProperties[] = $property; + } + } + + // This is a very nasty hack to make comparator work with the legacy json_array type, which should be killed in v3 + if ($this->isALegacyJsonComparison($properties1['type'], $properties2['type'])) { + array_shift($changedProperties); + + $changedProperties[] = 'comment'; + } + + if ($properties1['default'] != $properties2['default'] || + // Null values need to be checked additionally as they tell whether to create or drop a default value. + // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. + (null === $properties1['default'] && null !== $properties2['default']) || + (null === $properties2['default'] && null !== $properties1['default']) + ) { + $changedProperties[] = 'default'; + } + + if (($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) || + $properties1['type'] instanceof Types\BinaryType + ) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $properties1['length'] ?: 255; + $length2 = $properties2['length'] ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($properties1['fixed'] != $properties2['fixed']) { + $changedProperties[] = 'fixed'; + } + } elseif ($properties1['type'] instanceof Types\DecimalType) { + if (($properties1['precision'] ?: 10) != ($properties2['precision'] ?: 10)) { + $changedProperties[] = 'precision'; + } + if ($properties1['scale'] != $properties2['scale']) { + $changedProperties[] = 'scale'; + } + } + + // A null value and an empty string are actually equal for a comment so they should not trigger a change. + if ($properties1['comment'] !== $properties2['comment'] && + ! (null === $properties1['comment'] && '' === $properties2['comment']) && + ! (null === $properties2['comment'] && '' === $properties1['comment']) + ) { + $changedProperties[] = 'comment'; + } + + $customOptions1 = $column1->getCustomSchemaOptions(); + $customOptions2 = $column2->getCustomSchemaOptions(); + + foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) { + if ( ! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) { + $changedProperties[] = $key; + } elseif ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + $platformOptions1 = $column1->getPlatformOptions(); + $platformOptions2 = $column2->getPlatformOptions(); + + foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) { + if ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + return array_unique($changedProperties); + } + + /** + * TODO: kill with fire on v3.0 + * + * @deprecated + */ + private function isALegacyJsonComparison(Types\Type $one, Types\Type $other) : bool + { + if ( ! $one instanceof Types\JsonType || ! $other instanceof Types\JsonType) { + return false; + } + + return ( ! $one instanceof Types\JsonArrayType && $other instanceof Types\JsonArrayType) + || ( ! $other instanceof Types\JsonArrayType && $one instanceof Types\JsonArrayType); + } + + /** + * Finds the difference between the indexes $index1 and $index2. + * + * Compares $index1 with $index2 and returns $index2 if there are any + * differences or false in case there are no differences. + * + * @param \Doctrine\DBAL\Schema\Index $index1 + * @param \Doctrine\DBAL\Schema\Index $index2 + * + * @return boolean + */ + public function diffIndex(Index $index1, Index $index2) + { + if ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1)) { + return false; + } + + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Constraint.php b/wbce/include/Doctrine/DBAL/Schema/Constraint.php new file mode 100644 index 000000000..2e68a7710 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Marker interface for constraints. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Constraint +{ + /** + * @return string + */ + public function getName(); + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedName(AbstractPlatform $platform); + + /** + * Returns the names of the referencing table columns + * the constraint is associated with. + * + * @return array + */ + public function getColumns(); + + /** + * Returns the quoted representation of the column names + * the constraint is associated with. + * + * But only if they were defined with one or a column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform); +} diff --git a/wbce/include/Doctrine/DBAL/Schema/DB2SchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/DB2SchemaManager.php new file mode 100644 index 000000000..dc48e009f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,218 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * IBM Db2 Schema Manager. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * Apparently creator is the schema not the user who created it: + * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->filterAssetNames($this->_getPortableTablesList($tables)); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $default = null; + + if (null !== $tableColumn['default'] && 'NULL' != $tableColumn['default']) { + $default = trim($tableColumn['default'], "'"); + } + + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); + + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch (strtolower($tableColumn['typename'])) { + case 'varchar': + $length = $tableColumn['length']; + $fixed = false; + break; + case 'character': + $length = $tableColumn['length']; + $fixed = true; + break; + case 'clob': + $length = $tableColumn['length']; + break; + case 'decimal': + case 'double': + case 'real': + $scale = $tableColumn['scale']; + $precision = $tableColumn['length']; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => (bool) $fixed, + 'default' => $default, + 'autoincrement' => (boolean) $tableColumn['autoincrement'], + 'notnull' => (bool) ($tableColumn['nulls'] == 'N'), + 'scale' => null, + 'precision' => null, + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + 'platformOptions' => array(), + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['colname'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables as $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + + return $tableNames; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndexRow) { + $tableIndexRow = array_change_key_case($tableIndexRow, \CASE_LOWER); + $tableIndexRow['primary'] = (boolean) $tableIndexRow['primary']; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + $tableForeignKey = array_change_key_case($tableForeignKey, \CASE_LOWER); + + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } elseif ($def == "N") { + return "SET NULL"; + } + + return null; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + // sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199 + //$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']); + if (!is_resource($view['text'])) { + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + } else { + $sql = ''; + } + + return new View($view['name'], $sql); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/DrizzleSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/DrizzleSchemaManager.php new file mode 100644 index 000000000..e9503c359 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/DrizzleSchemaManager.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the Drizzle RDBMS. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtolower($tableColumn['DATA_TYPE']); + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + + $options = array( + 'notnull' => !(bool) $tableColumn['IS_NULLABLE'], + 'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'], + 'default' => isset($tableColumn['COLUMN_DEFAULT']) ? $tableColumn['COLUMN_DEFAULT'] : null, + 'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'], + 'scale' => (int) $tableColumn['NUMERIC_SCALE'], + 'precision' => (int) $tableColumn['NUMERIC_PRECISION'], + 'comment' => isset($tableColumn['COLUMN_COMMENT']) && '' !== $tableColumn['COLUMN_COMMENT'] + ? $tableColumn['COLUMN_COMMENT'] + : null, + ); + + $column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options); + + if ( ! empty($tableColumn['COLLATION_NAME'])) { + $column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['SCHEMA_NAME']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['TABLE_NAME']; + } + + /** + * {@inheritdoc} + */ + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $columns = array(); + foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { + $columns[] = trim($value, ' `'); + } + + $refColumns = array(); + foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { + $refColumns[] = trim($value, ' `'); + } + + return new ForeignKeyConstraint( + $columns, + $tableForeignKey['REFERENCED_TABLE_NAME'], + $refColumns, + $tableForeignKey['CONSTRAINT_NAME'], + array( + 'onUpdate' => $tableForeignKey['UPDATE_RULE'], + 'onDelete' => $tableForeignKey['DELETE_RULE'], + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + $indexes = array(); + foreach ($tableIndexes as $k) { + $k['primary'] = (boolean) $k['primary']; + $indexes[] = $k; + } + + return parent::_getPortableTableIndexesList($indexes, $tableName); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/wbce/include/Doctrine/DBAL/Schema/ForeignKeyConstraint.php new file mode 100644 index 000000000..6070eb2e5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,389 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * An abstraction class for a foreign key constraint. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.0 + */ +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * Instance of the referencing table the foreign key constraint is associated with. + * + * @var \Doctrine\DBAL\Schema\Table + */ + protected $_localTable; + + /** + * Asset identifier instances of the referencing table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_localColumnNames; + + /** + * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. + * + * @var Table|Identifier + */ + protected $_foreignTableName; + + /** + * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_foreignColumnNames; + + /** + * @var array Options associated with the foreign key constraint. + */ + protected $_options; + + /** + * Initializes the foreign key constraint. + * + * @param array $localColumnNames Names of the referencing table columns. + * @param Table|string $foreignTableName Referenced table. + * @param array $foreignColumnNames Names of the referenced table columns. + * @param string|null $name Name of the foreign key constraint. + * @param array $options Options associated with the foreign key constraint. + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = array()) + { + $this->_setName($name); + $identifierConstructorCallback = function ($column) { + return new Identifier($column); + }; + $this->_localColumnNames = $localColumnNames + ? array_combine($localColumnNames, array_map($identifierConstructorCallback, $localColumnNames)) + : array(); + + if ($foreignTableName instanceof Table) { + $this->_foreignTableName = $foreignTableName; + } else { + $this->_foreignTableName = new Identifier($foreignTableName); + } + + $this->_foreignColumnNames = $foreignColumnNames + ? array_combine($foreignColumnNames, array_map($identifierConstructorCallback, $foreignColumnNames)) + : array(); + $this->_options = $options; + } + + /** + * Returns the name of the referencing table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * Sets the Table instance of the referencing table + * the foreign key constraint is associated with. + * + * @param \Doctrine\DBAL\Schema\Table $table Instance of the referencing table. + * + * @return void + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return Table + */ + public function getLocalTable() + { + return $this->_localTable; + } + + /** + * Returns the names of the referencing table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getLocalColumns() + { + return array_keys($this->_localColumnNames); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedLocalColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_localColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns unquoted representation of local table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedLocalColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getLocalColumns()); + } + + /** + * Returns unquoted representation of foreign table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedForeignColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getForeignColumns()); + } + + /** + * {@inheritdoc} + * + * @see getLocalColumns + */ + public function getColumns() + { + return $this->getLocalColumns(); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @see getQuotedLocalColumns + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + return $this->getQuotedLocalColumns($platform); + } + + /** + * Returns the name of the referenced table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName->getName(); + } + + /** + * Returns the non-schema qualified foreign table name. + * + * @return string + */ + public function getUnqualifiedForeignTableName() + { + $parts = explode(".", $this->_foreignTableName->getName()); + + return strtolower(end($parts)); + } + + /** + * Returns the quoted representation of the referenced table name + * the foreign key constraint is associated with. + * + * But only if it was defined with one or the referenced table name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return string + */ + public function getQuotedForeignTableName(AbstractPlatform $platform) + { + return $this->_foreignTableName->getQuotedName($platform); + } + + /** + * Returns the names of the referenced table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getForeignColumns() + { + return array_keys($this->_foreignColumnNames); + } + + /** + * Returns the quoted representation of the referenced table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referenced table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedForeignColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_foreignColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns whether or not a given option + * is associated with the foreign key constraint. + * + * @param string $name Name of the option to check. + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * Returns an option associated with the foreign key constraint. + * + * @param string $name Name of the option the foreign key constraint is associated with. + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Returns the options associated with the foreign key constraint. + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Returns the referential action for UPDATE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onUpdate() + { + return $this->onEvent('onUpdate'); + } + + /** + * Returns the referential action for DELETE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onDelete() + { + return $this->onEvent('onDelete'); + } + + /** + * Returns the referential action for a given database operation + * on the referenced table the foreign key constraint is associated with. + * + * @param string $event Name of the database operation/event to return the referential action for. + * + * @return string|null + */ + private function onEvent($event) + { + if (isset($this->_options[$event])) { + $onEvent = strtoupper($this->_options[$event]); + + if ( ! in_array($onEvent, array('NO ACTION', 'RESTRICT'))) { + return $onEvent; + } + } + + return false; + } + + /** + * Checks whether this foreign key constraint intersects the given index columns. + * + * Returns `true` if at least one of this foreign key's local columns + * matches one of the given index's columns, `false` otherwise. + * + * @param Index $index The index to be checked against. + * + * @return boolean + */ + public function intersectsIndexColumns(Index $index) + { + foreach ($index->getColumns() as $indexColumn) { + foreach ($this->_localColumnNames as $localColumn) { + if (strtolower($indexColumn) === strtolower($localColumn->getName())) { + return true; + } + } + } + + return false; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Identifier.php b/wbce/include/Doctrine/DBAL/Schema/Identifier.php new file mode 100644 index 000000000..babe583e4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Identifier.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * An abstraction class for an asset identifier. + * + * Wraps identifier names like column names in indexes / foreign keys + * in an abstract class for proper quotation capabilities. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.4 + */ +class Identifier extends AbstractAsset +{ + /** + * Constructor. + * + * @param string $identifier Identifier name to wrap. + * @param bool $quote Whether to force quoting the given identifier. + */ + public function __construct($identifier, $quote = false) + { + $this->_setName($identifier); + + if ($quote && ! $this->_quoted) { + $this->_setName('"' . $this->getName() . '"'); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Index.php b/wbce/include/Doctrine/DBAL/Schema/Index.php new file mode 100644 index 000000000..554d80f78 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,358 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class Index extends AbstractAsset implements Constraint +{ + /** + * Asset identifier instances of the column names the index is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_columns = array(); + + /** + * @var boolean + */ + protected $_isUnique = false; + + /** + * @var boolean + */ + protected $_isPrimary = false; + + /** + * Platform specific flags for indexes. + * array($flagName => true) + * + * @var array + */ + protected $_flags = array(); + + /** + * Platform specific options + * + * @todo $_flags should eventually be refactored into options + * + * @var array + */ + private $options = array(); + + /** + * @param string $indexName + * @param string[] $columns + * @param boolean $isUnique + * @param boolean $isPrimary + * @param string[] $flags + * @param array $options + */ + public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array(), array $options = array()) + { + $isUnique = $isUnique || $isPrimary; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + $this->options = $options; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * @param string $column + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function _addColumn($column) + { + if (is_string($column)) { + $this->_columns[$column] = new Identifier($column); + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * {@inheritdoc} + */ + public function getColumns() + { + return array_keys($this->_columns); + } + + /** + * {@inheritdoc} + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_columns as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * @return string[] + */ + public function getUnquotedColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getColumns()); + } + + /** + * Is the index neither unique nor primary key? + * + * @return boolean + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return boolean + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return boolean + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param integer $pos + * + * @return boolean + */ + public function hasColumnAtPosition($columnName, $pos = 0) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); + + return array_search($columnName, $indexColumns) === $pos; + } + + /** + * Checks if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $columns = $this->getColumns(); + $numberOfColumns = count($columns); + $sameColumns = true; + + for ($i = 0; $i < $numberOfColumns; $i++) { + if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) { + $sameColumns = false; + } + } + + return $sameColumns; + } + + /** + * Checks if the other index already fulfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * + * @return boolean + */ + public function isFullfilledBy(Index $other) + { + // allow the other index to be equally large only. It being larger is an option + // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) + if (count($other->getColumns()) != count($this->getColumns())) { + return false; + } + + // Check if columns are the same, and even in the same order + $sameColumns = $this->spansColumns($other->getColumns()); + + if ($sameColumns) { + if ( ! $this->samePartialIndex($other)) { + return false; + } + + if ( ! $this->isUnique() && ! $this->isPrimary()) { + // this is a special case: If the current key is neither primary or unique, any unique or + // primary key will always have the same effect for the index and there cannot be any constraint + // overlaps. This means a primary or unique index can always fulfill the requirements of just an + // index that has no constraints. + return true; + } + + if ($other->isPrimary() != $this->isPrimary()) { + return false; + } + + if ($other->isUnique() != $this->isUnique()) { + return false; + } + + return true; + } + + return false; + } + + /** + * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * + * @return boolean + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } elseif ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other)) { + return true; + } + + return false; + } + + /** + * Returns platform specific flags for indexes. + * + * @return string[] + */ + public function getFlags() + { + return array_keys($this->_flags); + } + + /** + * Adds Flag for an index that translates to platform specific handling. + * + * @example $index->addFlag('CLUSTERED') + * + * @param string $flag + * + * @return Index + */ + public function addFlag($flag) + { + $this->_flags[strtolower($flag)] = true; + + return $this; + } + + /** + * Does this index have a specific flag? + * + * @param string $flag + * + * @return boolean + */ + public function hasFlag($flag) + { + return isset($this->_flags[strtolower($flag)]); + } + + /** + * Removes a flag. + * + * @param string $flag + * + * @return void + */ + public function removeFlag($flag) + { + unset($this->_flags[strtolower($flag)]); + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->options[strtolower($name)]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->options[strtolower($name)]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Return whether the two indexes have the same partial index + * @param \Doctrine\DBAL\Schema\Index $other + * + * @return boolean + */ + private function samePartialIndex(Index $other) + { + if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) { + return true; + } + + if ( ! $this->hasOption('where') && ! $other->hasOption('where')) { + return true; + } + + return false; + } + +} diff --git a/wbce/include/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/MySqlSchemaManager.php new file mode 100644 index 000000000..0b36762b6 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the MySql RDBMS. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + foreach ($tableIndexes as $k => $v) { + $v = array_change_key_case($v, CASE_LOWER); + if ($v['key_name'] == 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = array('FULLTEXT'); + } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + $v['flags'] = array('SPATIAL'); + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['type']); + $dbType = strtok($dbType, '(), '); + if (isset($tableColumn['length'])) { + $length = $tableColumn['length']; + } else { + $length = strtok('(), '); + } + + $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + // In cases where not connected to a database DESCRIBE $table does not return 'Comment' + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch ($dbType) { + case 'char': + case 'binary': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'numeric': + case 'decimal': + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'tinytext': + $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT; + break; + case 'text': + $length = MySqlPlatform::LENGTH_LIMIT_TEXT; + break; + case 'mediumtext': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT; + break; + case 'tinyblob': + $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB; + break; + case 'blob': + $length = MySqlPlatform::LENGTH_LIMIT_BLOB; + break; + case 'mediumblob': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB; + break; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'year': + $length = null; + break; + } + + $length = ((int) $length == 0) ? null : (int) $length; + + $options = array( + 'length' => $length, + 'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false), + 'fixed' => (bool) $fixed, + 'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null, + 'notnull' => (bool) ($tableColumn['null'] != 'YES'), + 'scale' => null, + 'precision' => null, + 'autoincrement' => (bool) (strpos($tableColumn['extra'], 'auto_increment') !== false), + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if (!isset($value['delete_rule']) || $value['delete_rule'] == "RESTRICT") { + $value['delete_rule'] = null; + } + if (!isset($value['update_rule']) || $value['update_rule'] == "RESTRICT") { + $value['update_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['referenced_table_name'], + 'onDelete' => $value['delete_rule'], + 'onUpdate' => $value['update_rule'], + ); + } + $list[$value['constraint_name']]['local'][] = $value['column_name']; + $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + ) + ); + } + + return $result; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/OracleSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/OracleSchemaManager.php new file mode 100644 index 000000000..9feecdc76 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,425 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Types\Type; + +/** + * Oracle Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + try { + parent::dropDatabase($database); + } catch (DBALException $exception) { + $exception = $exception->getPrevious(); + + if (! $exception instanceof DriverException) { + throw $exception; + } + + // If we have a error code 1940 (ORA-01940), the drop database operation failed + // because of active connections on the database. + // To force dropping the database, we first have to close all active connections + // on that database and issue the drop database operation again. + if ($exception->getErrorCode() !== 1940) { + throw $exception; + } + + $this->killUserSessions($database); + + parent::dropDatabase($database); + } + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $this->getQuotedIdentifierName($table['table_name']); + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + foreach ($tableIndexes as $tableIndex) { + $tableIndex = \array_change_key_case($tableIndex, CASE_LOWER); + + $keyName = strtolower($tableIndex['name']); + + if (strtolower($tableIndex['is_primary']) == "p") { + $keyName = 'primary'; + $buffer['primary'] = true; + $buffer['non_unique'] = false; + } else { + $buffer['primary'] = false; + $buffer['non_unique'] = ($tableIndex['is_unique'] == 0) ? true : false; + } + $buffer['key_name'] = $keyName; + $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); + $indexBuffer[] = $buffer; + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = \array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['data_type']); + if (strpos($dbType, "timestamp(") === 0) { + if (strpos($dbType, "with time zone")) { + $dbType = "timestamptz"; + } else { + $dbType = "timestamp"; + } + } + + $unsigned = $fixed = null; + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + // Default values returned from database sometimes have trailing spaces. + $tableColumn['data_default'] = trim($tableColumn['data_default']); + + if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { + $tableColumn['data_default'] = null; + } + + if (null !== $tableColumn['data_default']) { + // Default values returned from database are enclosed in single quotes. + $tableColumn['data_default'] = trim($tableColumn['data_default'], "'"); + } + + $precision = null; + $scale = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); + $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); + + switch ($dbType) { + case 'number': + if ($tableColumn['data_precision'] == 20 && $tableColumn['data_scale'] == 0) { + $precision = 20; + $scale = 0; + $type = 'bigint'; + } elseif ($tableColumn['data_precision'] == 5 && $tableColumn['data_scale'] == 0) { + $type = 'smallint'; + $precision = 5; + $scale = 0; + } elseif ($tableColumn['data_precision'] == 1 && $tableColumn['data_scale'] == 0) { + $precision = 1; + $scale = 0; + $type = 'boolean'; + } elseif ($tableColumn['data_scale'] > 0) { + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $type = 'decimal'; + } + $length = null; + break; + case 'pls_integer': + case 'binary_integer': + $length = null; + break; + case 'varchar': + case 'varchar2': + case 'nvarchar2': + $length = $tableColumn['char_length']; + $fixed = false; + break; + case 'char': + case 'nchar': + $length = $tableColumn['char_length']; + $fixed = true; + break; + case 'date': + case 'timestamp': + $length = null; + break; + case 'float': + case 'binary_float': + case 'binary_double': + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $length = null; + break; + case 'clob': + case 'nclob': + $length = null; + break; + case 'blob': + case 'raw': + case 'long raw': + case 'bfile': + $length = null; + break; + case 'rowid': + case 'urowid': + default: + $length = null; + } + + $options = array( + 'notnull' => (bool) ($tableColumn['nullable'] === 'N'), + 'fixed' => (bool) $fixed, + 'unsigned' => (bool) $unsigned, + 'default' => $tableColumn['data_default'], + 'length' => $length, + 'precision' => $precision, + 'scale' => $scale, + 'comment' => isset($tableColumn['comments']) && '' !== $tableColumn['comments'] + ? $tableColumn['comments'] + : null, + 'platformDetails' => array(), + ); + + return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = \array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if ($value['delete_rule'] == "NO ACTION") { + $value['delete_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $this->getQuotedIdentifierName($value['constraint_name']), + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + + $localColumn = $this->getQuotedIdentifierName($value['local_column']); + $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); + + $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; + $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $this->getQuotedIdentifierName($constraint['foreignTable']), + array_values($constraint['foreign']), $this->getQuotedIdentifierName($constraint['name']), + array('onDelete' => $constraint['onDelete']) + ); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + + return new Sequence( + $this->getQuotedIdentifierName($sequence['sequence_name']), + $sequence['increment_by'], + $sequence['min_value'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + + return $function['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + + return $database['username']; + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $username = $database; + $password = $params['password']; + + $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; + $this->_conn->executeUpdate($query); + + $query = 'GRANT DBA TO ' . $username; + $this->_conn->executeUpdate($query); + + return true; + } + + /** + * @param string $table + * + * @return boolean + */ + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function dropTable($name) + { + $this->tryMethod('dropAutoincrement', $name); + + parent::dropTable($name); + } + + /** + * Returns the quoted representation of the given identifier name. + * + * Quotes non-uppercase identifiers explicitly to preserve case + * and thus make references to the particular identifier work. + * + * @param string $identifier The identifier to quote. + * + * @return string The quoted identifier. + */ + private function getQuotedIdentifierName($identifier) + { + if (preg_match('/[a-z]/', $identifier)) { + return $this->_platform->quoteIdentifier($identifier); + } + + return $identifier; + } + + /** + * Kills sessions connected with the given user. + * + * This is useful to force DROP USER operations which could fail because of active user sessions. + * + * @param string $user The name of the user to kill sessions for. + * + * @return void + */ + private function killUserSessions($user) + { + $sql = <<_conn->fetchAll($sql, array(strtoupper($user))); + + foreach ($activeUserSessions as $activeUserSession) { + $activeUserSession = array_change_key_case($activeUserSession, \CASE_LOWER); + + $this->_execSql( + sprintf( + "ALTER SYSTEM KILL SESSION '%s, %s' IMMEDIATE", + $activeUserSession['sid'], + $activeUserSession['serial#'] + ) + ); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php new file mode 100644 index 000000000..395ad95af --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,469 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Types\Type; + +/** + * PostgreSQL Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Gets all the existing schema names. + * + * @return array + */ + public function getSchemaNames() + { + $rows = $this->_conn->fetchAll("SELECT nspname as schema_name FROM pg_namespace WHERE nspname !~ '^pg_.*' and nspname != 'information_schema'"); + + return array_map(function ($v) { return $v['schema_name']; }, $rows); + } + + /** + * Returns an array of schema search paths. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getSchemaSearchPaths() + { + $params = $this->_conn->getParams(); + $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path')); + + if (isset($params['user'])) { + $schema = str_replace('"$user"', $params['user'], $schema); + } + + return array_map('trim', $schema); + } + + /** + * Gets names of all existing schemas in the current users search path. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getExistingSchemaSearchPaths() + { + if ($this->existingSchemaPaths === null) { + $this->determineExistingSchemaSearchPaths(); + } + + return $this->existingSchemaPaths; + } + + /** + * Sets or resets the order of the existing schemas in the current search path of the user. + * + * This is a PostgreSQL only function. + * + * @return void + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->getSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) { + return in_array($v, $names); + }); + } + + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + try { + parent::dropDatabase($database); + } catch (DriverException $exception) { + // If we have a SQLSTATE 55006, the drop database operation failed + // because of active connections on the database. + // To force dropping the database, we first have to close all active connections + // on that database and issue the drop database operation again. + if ($exception->getSQLState() !== '55006') { + throw $exception; + } + + $this->_execSql( + array( + $this->_platform->getDisallowDatabaseConnectionsSQL($database), + $this->_platform->getCloseActiveDatabaseConnectionsSQL($database), + ) + ); + + parent::dropDatabase($database); + } + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $onUpdate = null; + $onDelete = null; + + if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onUpdate = $match[1]; + } + if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onDelete = $match[1]; + } + + if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) { + // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get + // the idea to trim them here. + $localColumns = array_map('trim', explode(",", $values[1])); + $foreignColumns = array_map('trim', explode(",", $values[3])); + $foreignTable = $values[2]; + } + + return new ForeignKeyConstraint( + $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], + array('onUpdate' => $onUpdate, 'onDelete' => $onDelete) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['schemaname'].'.'.$view['viewname'], $view['definition']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $schemas = $this->getExistingSchemaSearchPaths(); + $firstSchema = array_shift($schemas); + + if ($table['schema_name'] == $firstSchema) { + return $table['table_name']; + } else { + return $table['schema_name'] . "." . $table['table_name']; + } + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $buffer = array(); + foreach ($tableIndexes as $row) { + $colNumbers = explode(' ', $row['indkey']); + $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )'; + $columnNameSql = "SELECT attnum, attname FROM pg_attribute + WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;"; + + $stmt = $this->_conn->executeQuery($columnNameSql); + $indexColumns = $stmt->fetchAll(); + + // required for getting the order of the columns right. + foreach ($colNumbers as $colNum) { + foreach ($indexColumns as $colRow) { + if ($colNum == $colRow['attnum']) { + $buffer[] = array( + 'key_name' => $row['relname'], + 'column_name' => trim($colRow['attname']), + 'non_unique' => !$row['indisunique'], + 'primary' => $row['indisprimary'], + 'where' => $row['where'], + ); + } + } + } + } + + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequencesList($sequences) + { + $sequenceDefinitions = array(); + + foreach ($sequences as $sequence) { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $sequenceDefinitions[$sequenceName] = $sequence; + } + + $list = array(); + + foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { + $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['nspname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); + + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); + $tableColumn['length'] = $length; + } + + $matches = array(); + + $autoincrement = false; + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { + $tableColumn['sequence'] = $matches[1]; + $tableColumn['default'] = null; + $autoincrement = true; + } + + if (preg_match("/^['(](.*)[')]::.*$/", $tableColumn['default'], $matches)) { + $tableColumn['default'] = $matches[1]; + } + + if (stripos($tableColumn['default'], 'NULL') === 0) { + $tableColumn['default'] = null; + } + + $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; + if ($length == '-1' && isset($tableColumn['atttypmod'])) { + $length = $tableColumn['atttypmod'] - 4; + } + if ((int) $length <= 0) { + $length = null; + } + $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + $jsonb = null; + + $dbType = strtolower($tableColumn['type']); + if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { + $dbType = strtolower($tableColumn['domain_type']); + $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'smallint': + case 'int2': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + case 'int': + case 'int4': + case 'integer': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + case 'bigint': + case 'int8': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + $length = null; + break; + case 'bool': + case 'boolean': + if ($tableColumn['default'] === 'true') { + $tableColumn['default'] = true; + } + + if ($tableColumn['default'] === 'false') { + $tableColumn['default'] = false; + } + + $length = null; + break; + case 'text': + $fixed = false; + break; + case 'varchar': + case 'interval': + case '_varchar': + $fixed = false; + break; + case 'char': + case 'bpchar': + $fixed = true; + break; + case 'float': + case 'float4': + case 'float8': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'money': + case 'numeric': + $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); + + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'year': + $length = null; + break; + + // PostgreSQL 9.4+ only + case 'jsonb': + $jsonb = true; + break; + } + + if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { + $tableColumn['default'] = $match[1]; + } + + $options = array( + 'length' => $length, + 'notnull' => (bool) $tableColumn['isnotnull'], + 'default' => $tableColumn['default'], + 'primary' => (bool) ($tableColumn['pri'] == 't'), + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ); + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && !empty($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + if (in_array($column->getType()->getName(), [Type::JSON_ARRAY, Type::JSON], true)) { + $column->setPlatformOption('jsonb', $jsonb); + } + + return $column; + } + + /** + * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. + * + * @param mixed $defaultValue + * + * @return mixed + */ + private function fixVersion94NegativeNumericDefaultValue($defaultValue) + { + if (strpos($defaultValue, '(') === 0) { + return trim($defaultValue, '()'); + } + + return $defaultValue; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php new file mode 100644 index 000000000..793a5fe8b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * SAP Sybase SQL Anywhere schema manager. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * Starts a database after creation + * as SQL Anywhere needs a database to be started + * before it can be used. + * + * @see startDatabase + */ + public function createDatabase($database) + { + parent::createDatabase($database); + $this->startDatabase($database); + } + + /** + * {@inheritdoc} + * + * Tries stopping a database before dropping + * as SQL Anywhere needs a database to be stopped + * before it can be dropped. + * + * @see stopDatabase + */ + public function dropDatabase($database) + { + $this->tryMethod('stopDatabase', $database); + parent::dropDatabase($database); + } + + /** + * Starts a database. + * + * @param string $database The name of the database to start. + */ + public function startDatabase($database) + { + $this->_execSql($this->_platform->getStartDatabaseSQL($database)); + } + + /** + * Stops a database. + * + * @param string $database The name of the database to stop. + */ + public function stopDatabase($database) + { + $this->_execSql($this->_platform->getStopDatabaseSQL($database)); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['start_with']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['type']); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + $precision = null; + $scale = null; + $fixed = false; + $default = null; + + if (null !== $tableColumn['default']) { + // Strip quotes from default value. + $default = preg_replace(array("/^'(.*)'$/", "/''/"), array("$1", "'"), $tableColumn['default']); + + if ('autoincrement' == $default) { + $default = null; + } + } + + switch ($tableColumn['type']) { + case 'binary': + case 'char': + case 'nchar': + $fixed = true; + } + + switch ($type) { + case 'decimal': + case 'float': + $precision = $tableColumn['length']; + $scale = $tableColumn['scale']; + } + + return new Column( + $tableColumn['column_name'], + Type::getType($type), + array( + 'length' => $type == 'string' ? $tableColumn['length'] : null, + 'precision' => $precision, + 'scale' => $scale, + 'unsigned' => (bool) $tableColumn['unsigned'], + 'fixed' => $fixed, + 'notnull' => (bool) $tableColumn['notnull'], + 'default' => $default, + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => isset($tableColumn['comment']) && '' !== $tableColumn['comment'] + ? $tableColumn['comment'] + : null, + )); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['table_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'notnull' => $tableForeignKey['notnull'], + 'match' => $tableForeignKey['match'], + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + 'check_on_commit' => $tableForeignKey['check_on_commit'], + 'clustered' => $tableForeignKey['clustered'], + 'for_olap_workload' => $tableForeignKey['for_olap_workload'] + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = array(); + + if ($tableIndex['clustered']) { + $tableIndex['flags'][] = 'clustered'; + } + + if ($tableIndex['with_nulls_not_distinct']) { + $tableIndex['flags'][] = 'with_nulls_not_distinct'; + } + + if ($tableIndex['for_olap_workload']) { + $tableIndex['flags'][] = 'for_olap_workload'; + } + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View( + $view['table_name'], + preg_replace('/^.*\s+as\s+SELECT(.*)/i', "SELECT$1", $view['view_def']) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/SQLServerSchemaManager.php new file mode 100644 index 000000000..100a64c22 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,311 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Types\Type; + +/** + * SQL Server Schema Manager. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @author Steve Müller + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + try { + parent::dropDatabase($database); + } catch (DBALException $exception) { + $exception = $exception->getPrevious(); + + if (! $exception instanceof DriverException) { + throw $exception; + } + + // If we have a error code 3702, the drop database operation failed + // because of active connections on the database. + // To force dropping the database, we first have to close all active connections + // on that database and issue the drop database operation again. + if ($exception->getErrorCode() !== 3702) { + throw $exception; + } + + $this->closeActiveDatabaseConnections($database); + + parent::dropDatabase($database); + } + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['name'], $sequence['increment'], $sequence['start_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtok($tableColumn['type'], '(), '); + $fixed = null; + $length = (int) $tableColumn['length']; + $default = $tableColumn['default']; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + + if ($default == 'getdate()') { + $default = $this->_platform->getCurrentTimestampSQL(); + } + } + + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + case 'varchar': + // TEXT type is returned as VARCHAR(MAX) with a length of -1 + if ($length == -1) { + $dbType = 'text'; + } + break; + } + + if ('char' === $dbType || 'nchar' === $dbType || 'binary' === $dbType) { + $fixed = true; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => false, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) $tableColumn['notnull'], + 'scale' => $tableColumn['scale'], + 'precision' => $tableColumn['precision'], + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, + ); + + $column = new Column($tableColumn['name'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if ( ! isset($foreignKeys[$tableForeignKey['ForeignKey']])) { + $foreignKeys[$tableForeignKey['ForeignKey']] = array( + 'local_columns' => array($tableForeignKey['ColumnName']), + 'foreign_table' => $tableForeignKey['ReferenceTableName'], + 'foreign_columns' => array($tableForeignKey['ReferenceColumnName']), + 'name' => $tableForeignKey['ForeignKey'], + 'options' => array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']) + ) + ); + } else { + $foreignKeys[$tableForeignKey['ForeignKey']]['local_columns'][] = $tableForeignKey['ColumnName']; + $foreignKeys[$tableForeignKey['ForeignKey']]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['non_unique'] = (boolean) $tableIndex['non_unique']; + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = $tableIndex['flags'] ? array($tableIndex['flags']) : null; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * {@inheritdoc} + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + try { + $tableIndexes = $this->_conn->fetchAll($sql); + } catch (\PDOException $e) { + if ($e->getCode() == "IMSSP") { + return array(); + } else { + throw $e; + } + } catch (DBALException $e) { + if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { + return array(); + } else { + throw $e; + } + } + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * {@inheritdoc} + */ + public function alterTable(TableDiff $tableDiff) + { + if (count($tableDiff->removedColumns) > 0) { + foreach ($tableDiff->removedColumns as $col) { + $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); + foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) { + $this->_conn->exec("ALTER TABLE $tableDiff->name DROP CONSTRAINT " . $constraint['Name']); + } + } + } + + parent::alterTable($tableDiff); + } + + /** + * Returns the SQL to retrieve the constraints for a given column. + * + * @param string $table + * @param string $column + * + * @return string + */ + private function getColumnConstraintSQL($table, $column) + { + return "SELECT SysObjects.[Name] + FROM SysObjects INNER JOIN (SELECT [Name],[ID] FROM SysObjects WHERE XType = 'U') AS Tab + ON Tab.[ID] = Sysobjects.[Parent_Obj] + INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = Sysobjects.[ID] + INNER JOIN SysColumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID] + WHERE Col.[Name] = " . $this->_conn->quote($column) ." AND Tab.[Name] = " . $this->_conn->quote($table) . " + ORDER BY Col.[Name]"; + } + + /** + * Closes currently active connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to close currently active connections for. + * + * @return void + */ + private function closeActiveDatabaseConnections($database) + { + $database = new Identifier($database); + + $this->_execSql( + sprintf( + 'ALTER DATABASE %s SET SINGLE_USER WITH ROLLBACK IMMEDIATE', + $database->getQuotedName($this->_platform) + ) + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Schema.php b/wbce/include/Doctrine/DBAL/Schema/Schema.php new file mode 100644 index 000000000..3277ad72c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,512 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Object representation of a database schema. + * + * Different vendors have very inconsistent naming with regard to the concept + * of a "schema". Doctrine understands a schema as the entity that conceptually + * wraps a set of database objects such as tables, sequences, indexes and + * foreign keys that belong to each other into a namespace. A Doctrine Schema + * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more + * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL. + * + * Every asset in the doctrine schema has a name. A name consists of either a + * namespace.local name pair or just a local unqualified name. + * + * The abstraction layer that covers a PostgreSQL schema is the namespace of an + * database object (asset). A schema can have a name, which will be used as + * default namespace for the unqualified database objects that are created in + * the schema. + * + * In the case of MySQL where cross-database queries are allowed this leads to + * databases being "misinterpreted" as namespaces. This is intentional, however + * the CREATE/DROP SQL visitors will just filter this queries and do not + * execute them. Only the queries for the currently connected database are + * executed. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Schema extends AbstractAsset +{ + /** + * The namespaces in this schema. + * + * @var array + */ + private $namespaces = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table[] + */ + protected $_tables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + protected $_sequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param \Doctrine\DBAL\Schema\Table[] $tables + * @param \Doctrine\DBAL\Schema\Sequence[] $sequences + * @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig + * @param array $namespaces + */ + public function __construct( + array $tables = array(), + array $sequences = array(), + SchemaConfig $schemaConfig = null, + array $namespaces = array() + ) { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($namespaces as $namespace) { + $this->createNamespace($namespace); + } + + foreach ($tables as $table) { + $this->_addTable($table); + } + + foreach ($sequences as $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addTable(Table $table) + { + $namespaceName = $table->getNamespaceName(); + $tableName = $table->getFullQualifiedName($this->getName()); + + if (isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addSequence(Sequence $sequence) + { + $namespaceName = $sequence->getNamespaceName(); + $seqName = $sequence->getFullQualifiedName($this->getName()); + + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + + if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_sequences[$seqName] = $sequence; + } + + /** + * Returns the namespaces of this schema. + * + * @return array A list of namespace names. + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Gets all tables of this schema. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @param string $name + * + * @return string + */ + private function getFullQualifiedAssetName($name) + { + $name = $this->getUnquotedAssetName($name); + + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + + return strtolower($name); + } + + /** + * Returns the unquoted representation of a given asset name. + * + * @param string $assetName Quoted or unquoted representation of an asset name. + * + * @return string + */ + private function getUnquotedAssetName($assetName) + { + if ($this->isIdentifierQuoted($assetName)) { + return $this->trimQuotes($assetName); + } + + return $assetName; + } + + /** + * Does this schema have a namespace with the given name? + * + * @param string $namespaceName + * + * @return boolean + */ + public function hasNamespace($namespaceName) + { + $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + return isset($this->namespaces[$namespaceName]); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * + * @return boolean + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + + return isset($this->_tables[$tableName]); + } + + /** + * Gets all table names, prefixed with a schema name, even the default one if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + /** + * @param string $sequenceName + * + * @return boolean + */ + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + + return isset($this->_sequences[$sequenceName]); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function getSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + if (!$this->hasSequence($sequenceName)) { + throw SchemaException::sequenceDoesNotExist($sequenceName); + } + + return $this->_sequences[$sequenceName]; + } + + /** + * @return \Doctrine\DBAL\Schema\Sequence[] + */ + public function getSequences() + { + return $this->_sequences; + } + + /** + * Creates a new namespace. + * + * @param string $namespaceName The name of the namespace to create. + * + * @return \Doctrine\DBAL\Schema\Schema This schema instance. + * + * @throws SchemaException + */ + public function createNamespace($namespaceName) + { + $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + if (isset($this->namespaces[$unquotedNamespaceName])) { + throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName); + } + + $this->namespaces[$unquotedNamespaceName] = $namespaceName; + + return $this; + } + + /** + * Creates a new table. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + */ + public function createTable($tableName) + { + $table = new Table($tableName); + $this->_addTable($table); + + foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) { + $table->addOption($name, $value); + } + + return $table; + } + + /** + * Renames a table. + * + * @param string $oldTableName + * @param string $newTableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + + return $this; + } + + /** + * Drops a table from the schema. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $this->getTable($tableName); + unset($this->_tables[$tableName]); + + return $this; + } + + /** + * Creates a new sequence. + * + * @param string $sequenceName + * @param integer $allocationSize + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + + return $seq; + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + + return $this; + } + + /** + * Returns an array of necessary SQL queries to create the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + $sqlCollector = new CreateSchemaSqlCollector($platform); + $this->visit($sqlCollector); + + return $sqlCollector->getQueries(); + } + + /** + * Return an array of necessary SQL queries to drop the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toDropSql(AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + if ($visitor instanceof NamespaceVisitor) { + foreach ($this->namespaces as $namespace) { + $visitor->acceptNamespace($namespace); + } + } + + foreach ($this->_tables as $table) { + $table->visit($visitor); + } + + foreach ($this->_sequences as $sequence) { + $sequence->visit($visitor); + } + } + + /** + * Cloning a Schema triggers a deep clone of all related assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_tables as $k => $table) { + $this->_tables[$k] = clone $table; + } + foreach ($this->_sequences as $k => $sequence) { + $this->_sequences[$k] = clone $sequence; + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SchemaConfig.php b/wbce/include/Doctrine/DBAL/Schema/SchemaConfig.php new file mode 100644 index 000000000..cc23237d8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var boolean + */ + protected $hasExplicitForeignKeyIndexes = false; + + /** + * @var integer + */ + protected $maxIdentifierLength = 63; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $defaultTableOptions = array(); + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->hasExplicitForeignKeyIndexes; + } + + /** + * @param boolean $flag + * + * @return void + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->hasExplicitForeignKeyIndexes = (bool) $flag; + } + + /** + * @param integer $length + * + * @return void + */ + public function setMaxIdentifierLength($length) + { + $this->maxIdentifierLength = (int) $length; + } + + /** + * @return integer + */ + public function getMaxIdentifierLength() + { + return $this->maxIdentifierLength; + } + + /** + * Gets the default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the default namespace name of schema objects. + * + * @param string $name The value to set. + * + * @return void + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the default options that are passed to Table instances created with + * Schema#createTable(). + * + * @return array + */ + public function getDefaultTableOptions() + { + return $this->defaultTableOptions; + } + + /** + * @param array $defaultTableOptions + * + * @return void + */ + public function setDefaultTableOptions(array $defaultTableOptions) + { + $this->defaultTableOptions = $defaultTableOptions; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SchemaDiff.php b/wbce/include/Doctrine/DBAL/Schema/SchemaDiff.php new file mode 100644 index 000000000..23eccdad8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,204 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Schema Diff. + * + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaDiff +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + public $fromSchema; + + /** + * All added namespaces. + * + * @var string[] + */ + public $newNamespaces = array(); + + /** + * All removed namespaces. + * + * @var string[] + */ + public $removedNamespaces = array(); + + /** + * All added tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $newTables = array(); + + /** + * All changed tables. + * + * @var \Doctrine\DBAL\Schema\TableDiff[] + */ + public $changedTables = array(); + + /** + * All removed tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $removedTables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $newSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $changedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $removedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param \Doctrine\DBAL\Schema\Table[] $newTables + * @param \Doctrine\DBAL\Schema\TableDiff[] $changedTables + * @param \Doctrine\DBAL\Schema\Table[] $removedTables + * @param \Doctrine\DBAL\Schema\Schema|null $fromSchema + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array(), Schema $fromSchema = null) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + $this->fromSchema = $fromSchema; + } + + /** + * The to save sql mode ensures that the following things don't happen: + * + * 1. Tables are deleted + * 2. Sequences are deleted + * 3. Foreign Keys which reference tables that would otherwise be deleted. + * + * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @param boolean $saveMode + * + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + if ($platform->supportsSchemas()) { + foreach ($this->newNamespaces as $newNamespace) { + $sql[] = $platform->getCreateSchemaSQL($newNamespace); + } + } + + if ($platform->supportsForeignKeyConstraints() && $saveMode == false) { + foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { + $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTable()); + } + } + + if ($platform->supportsSequences() == true) { + foreach ($this->changedSequences as $sequence) { + $sql[] = $platform->getAlterSequenceSQL($sequence); + } + + if ($saveMode === false) { + foreach ($this->removedSequences as $sequence) { + $sql[] = $platform->getDropSequenceSQL($sequence); + } + } + + foreach ($this->newSequences as $sequence) { + $sql[] = $platform->getCreateSequenceSQL($sequence); + } + } + + $foreignKeySql = array(); + foreach ($this->newTables as $table) { + $sql = array_merge( + $sql, + $platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) + ); + + if ($platform->supportsForeignKeyConstraints()) { + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table); + } + } + } + $sql = array_merge($sql, $foreignKeySql); + + if ($saveMode === false) { + foreach ($this->removedTables as $table) { + $sql[] = $platform->getDropTableSQL($table); + } + } + + foreach ($this->changedTables as $tableDiff) { + $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); + } + + return $sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SchemaException.php b/wbce/include/Doctrine/DBAL/Schema/SchemaException.php new file mode 100644 index 000000000..152f374da --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +class SchemaException extends \Doctrine\DBAL\DBALException +{ + const TABLE_DOESNT_EXIST = 10; + const TABLE_ALREADY_EXISTS = 20; + const COLUMN_DOESNT_EXIST = 30; + const COLUMN_ALREADY_EXISTS = 40; + const INDEX_DOESNT_EXIST = 50; + const INDEX_ALREADY_EXISTS = 60; + const SEQUENCE_DOENST_EXIST = 70; + const SEQUENCE_ALREADY_EXISTS = 80; + const INDEX_INVALID_NAME = 90; + const FOREIGNKEY_DOESNT_EXIST = 100; + const NAMESPACE_ALREADY_EXISTS = 110; + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableDoesNotExist($tableName) + { + return new self("There is no table with name '".$tableName."' in the schema.", self::TABLE_DOESNT_EXIST); + } + + /** + * @param string $indexName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexNameInvalid($indexName) + { + return new self("Invalid index-name $indexName given, has to be [a-zA-Z0-9_]", self::INDEX_INVALID_NAME); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexDoesNotExist($indexName, $table) + { + return new self("Index '$indexName' does not exist on table '$table'.", self::INDEX_DOESNT_EXIST); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexAlreadyExists($indexName, $table) + { + return new self("An index with name '$indexName' was already defined on table '$table'.", self::INDEX_ALREADY_EXISTS); + } + + /** + * @param string $columnName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnDoesNotExist($columnName, $table) + { + return new self("There is no column with name '$columnName' on table '$table'.", self::COLUMN_DOESNT_EXIST); + } + + /** + * @param string $namespaceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namespaceAlreadyExists($namespaceName) + { + return new self( + sprintf("The namespace with name '%s' already exists.", $namespaceName), + self::NAMESPACE_ALREADY_EXISTS + ); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableAlreadyExists($tableName) + { + return new self("The table with name '".$tableName."' already exists.", self::TABLE_ALREADY_EXISTS); + } + + /** + * @param string $tableName + * @param string $columnName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnAlreadyExists($tableName, $columnName) + { + return new self( + "The column '".$columnName."' on table '".$tableName."' already exists.", self::COLUMN_ALREADY_EXISTS + ); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceAlreadyExists($sequenceName) + { + return new self("The sequence '".$sequenceName."' already exists.", self::SEQUENCE_ALREADY_EXISTS); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceDoesNotExist($sequenceName) + { + return new self("There exists no sequence with the name '".$sequenceName."'.", self::SEQUENCE_DOENST_EXIST); + } + + /** + * @param string $fkName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function foreignKeyDoesNotExist($fkName, $table) + { + return new self("There exists no foreign key with the name '$fkName' on table '$table'.", self::FOREIGNKEY_DOESNT_EXIST); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey) + { + return new self( + "The performed schema operation on ".$localTable->getName()." requires a named foreign key, ". + "but the given foreign key from (".implode(", ", $foreignKey->getColumns()).") onto foreign table ". + "'".$foreignKey->getForeignTableName()."' (".implode(", ", $foreignKey->getForeignColumns()).") is currently ". + "unnamed." + ); + } + + /** + * @param string $changeName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function alterTableChangeNotSupported($changeName) + { + return new self("Alter table change not supported, given '$changeName'"); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Sequence.php b/wbce/include/Doctrine/DBAL/Schema/Sequence.php new file mode 100644 index 000000000..98c9cd1c5 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence structure. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var integer + */ + protected $allocationSize = 1; + + /** + * @var integer + */ + protected $initialValue = 1; + + /** + * @var integer|null + */ + protected $cache = null; + + /** + * @param string $name + * @param integer $allocationSize + * @param integer $initialValue + * @param integer|null $cache + */ + public function __construct($name, $allocationSize = 1, $initialValue = 1, $cache = null) + { + $this->_setName($name); + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + $this->cache = $cache; + } + + /** + * @return integer + */ + public function getAllocationSize() + { + return $this->allocationSize; + } + + /** + * @return integer + */ + public function getInitialValue() + { + return $this->initialValue; + } + + /** + * @return integer|null + */ + public function getCache() + { + return $this->cache; + } + + /** + * @param integer $allocationSize + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setAllocationSize($allocationSize) + { + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + + return $this; + } + + /** + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setInitialValue($initialValue) + { + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + + return $this; + } + + /** + * @param integer $cache + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setCache($cache) + { + $this->cache = $cache; + + return $this; + } + + /** + * Checks if this sequence is an autoincrement sequence for a given table. + * + * This is used inside the comparator to not report sequences as missing, + * when the "from" schema implicitly creates the sequences. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return boolean + */ + public function isAutoIncrementsFor(Table $table) + { + if ( ! $table->hasPrimaryKey()) { + return false; + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + if (count($pkColumns) != 1) { + return false; + } + + $column = $table->getColumn($pkColumns[0]); + + if ( ! $column->getAutoincrement()) { + return false; + } + + $sequenceName = $this->getShortestName($table->getNamespaceName()); + $tableName = $table->getShortestName($table->getNamespaceName()); + $tableSequenceName = sprintf('%s_%s_seq', $tableName, $column->getShortestName($table->getNamespaceName())); + + return $tableSequenceName === $sequenceName; + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSequence($this); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/wbce/include/Doctrine/DBAL/Schema/SqliteSchemaManager.php new file mode 100644 index 000000000..b3f0ef2bd --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,477 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Types\StringType; +use Doctrine\DBAL\Types\TextType; +use Doctrine\DBAL\Types\Type; + +/** + * Sqlite SchemaManager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @author Martin Hasoň + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $driver = $params['driver']; + $options = array( + 'driver' => $driver, + 'path' => $database + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($options); + $conn->connect(); + $conn->close(); + } + + /** + * {@inheritdoc} + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->fromTable = $this->listTableDetails($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->addedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->changedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropForeignKey($foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->removedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function listTableForeignKeys($table, $database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + if ( ! empty($tableForeignKeys)) { + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + + if (preg_match_all('# + (?:CONSTRAINT\s+([^\s]+)\s+)? + (?:FOREIGN\s+KEY[^\)]+\)\s*)? + REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))? + (?: + [^,]*? + (NOT\s+DEFERRABLE|DEFERRABLE) + (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? + )?#isx', + $createSql, $match)) { + + $names = array_reverse($match[1]); + $deferrable = array_reverse($match[2]); + $deferred = array_reverse($match[3]); + } else { + $names = $deferrable = $deferred = array(); + } + + foreach ($tableForeignKeys as $key => $value) { + $id = $value['id']; + $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id; + $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false; + $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false; + } + } + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery("PRAGMA TABLE_INFO ('$tableName')"); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + usort($indexArray, function($a, $b) { + if ($a['pk'] == $b['pk']) { + return $a['cid'] - $b['cid']; + } + + return $a['pk'] - $b['pk']; + }); + foreach ($indexArray as $indexColumnRow) { + if ($indexColumnRow['pk'] != "0") { + $indexBuffer[] = array( + 'key_name' => 'primary', + 'primary' => true, + 'non_unique' => false, + 'column_name' => $indexColumnRow['name'] + ); + } + } + + // fetch regular indexes + foreach ($tableIndexes as $tableIndex) { + // Ignore indexes with reserved names, e.g. autoindexes + if (strpos($tableIndex['name'], 'sqlite_') !== 0) { + $keyName = $tableIndex['name']; + $idx = array(); + $idx['key_name'] = $keyName; + $idx['primary'] = false; + $idx['non_unique'] = $tableIndex['unique']?false:true; + + $stmt = $this->_conn->executeQuery("PRAGMA INDEX_INFO ('{$keyName}')"); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + foreach ($indexArray as $indexColumnRow) { + $idx['column_name'] = $indexColumnRow['name']; + $indexBuffer[] = $idx; + } + } + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); + + // find column with autoincrement + $autoincrementColumn = null; + $autoincrementCount = 0; + + foreach ($tableColumns as $tableColumn) { + if ('0' != $tableColumn['pk']) { + $autoincrementCount++; + if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) { + $autoincrementColumn = $tableColumn['name']; + } + } + } + + if (1 == $autoincrementCount && null !== $autoincrementColumn) { + foreach ($list as $column) { + if ($autoincrementColumn == $column->getName()) { + $column->setAutoincrement(true); + } + } + } + + // inspect column collation and comments + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + + foreach ($list as $columnName => $column) { + $type = $column->getType(); + + if ($type instanceof StringType || $type instanceof TextType) { + $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY'); + } + + $comment = $this->parseColumnCommentFromSQL($columnName, $createSql); + + if (false !== $comment) { + $type = $this->extractDoctrineTypeFromComment($comment, null); + + if (null !== $type) { + $column->setType(Type::getType($type)); + + $comment = $this->removeDoctrineTypeFromComment($comment, $type); + } + + $column->setComment($comment); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $parts = explode('(', $tableColumn['type']); + $tableColumn['type'] = trim($parts[0]); + if (isset($parts[1])) { + $length = trim($parts[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = false; + + if (strpos($dbType, ' unsigned') !== false) { + $dbType = str_replace(' unsigned', '', $dbType); + $unsigned = true; + } + + $fixed = false; + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $default = $tableColumn['dflt_value']; + if ($default == 'NULL') { + $default = null; + } + if ($default !== null) { + // SQLite returns strings wrapped in single quotes, so we need to strip them + $default = preg_replace("/^'(.*)'$/", '\1', $default); + } + $notnull = (bool) $tableColumn['notnull']; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'decimal': + case 'numeric': + if (isset($tableColumn['length'])) { + if (strpos($tableColumn['length'], ',') === false) { + $tableColumn['length'] .= ",0"; + } + list($precision, $scale) = array_map('trim', explode(',', $tableColumn['length'])); + } + $length = null; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => $fixed, + 'notnull' => $notnull, + 'default' => $default, + 'precision' => $precision, + 'scale' => $scale, + 'autoincrement' => false, + ); + + return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + $name = $value['constraint_name']; + if ( ! isset($list[$name])) { + if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") { + $value['on_delete'] = null; + } + if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") { + $value['on_update'] = null; + } + + $list[$name] = array( + 'name' => $name, + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['table'], + 'onDelete' => $value['on_delete'], + 'onUpdate' => $value['on_update'], + 'deferrable' => $value['deferrable'], + 'deferred'=> $value['deferred'], + ); + } + $list[$name]['local'][] = $value['from']; + $list[$name]['foreign'][] = $value['to']; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + 'deferrable' => $constraint['deferrable'], + 'deferred'=> $constraint['deferred'], + ) + ); + } + + return $result; + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return \Doctrine\DBAL\Schema\TableDiff + * + * @throws \Doctrine\DBAL\DBALException + */ + private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + if ( ! $table instanceof Table) { + $tableDetails = $this->tryMethod('listTableDetails', $table); + if (false === $table) { + throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table)); + } + + $table = $tableDetails; + } + + $tableDiff = new TableDiff($table->getName()); + $tableDiff->fromTable = $table; + + return $tableDiff; + } + + /** + * @param string $column + * @param string $sql + * + * @return string|false + */ + private function parseColumnCollationFromSQL($column, $sql) + { + if (preg_match( + '{(?:'.preg_quote($column).'|'.preg_quote($this->_platform->quoteSingleIdentifier($column)).') + [^,(]+(?:\([^()]+\)[^,]*)? + (?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)* + COLLATE\s+["\']?([^\s,"\')]+)}isx', $sql, $match)) { + return $match[1]; + } + + return false; + } + + /** + * @param string $column + * @param string $sql + * + * @return string|false + */ + private function parseColumnCommentFromSQL($column, $sql) + { + if (preg_match( + '{[\s(,](?:'.preg_quote($this->_platform->quoteSingleIdentifier($column)).'|'.preg_quote($column).') + (?:\(.*?\)|[^,(])*?,?((?:\s*--[^\n]*\n?)+) + }isx', $sql, $match + )) { + $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); + + return '' === $comment ? false : $comment; + } + + return false; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php new file mode 100644 index 000000000..3158df47d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; + +/** + * Abstract schema synchronizer with methods for executing batches of SQL. + */ +abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + /** + * @param array $sql + */ + protected function processSqlSafely(array $sql) + { + foreach ($sql as $s) { + try { + $this->conn->exec($s); + } catch (\Exception $e) { + + } + } + } + + /** + * @param array $sql + */ + protected function processSql(array $sql) + { + foreach ($sql as $s) { + $this->conn->exec($s); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php new file mode 100644 index 000000000..1a5552ea6 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Schema\Schema; + +/** + * The synchronizer knows how to synchronize a schema with the configured + * database. + * + * @author Benjamin Eberlei + */ +interface SchemaSynchronizer +{ + /** + * Gets the SQL statements that can be executed to create the schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return array + */ + function getCreateSchema(Schema $createSchema); + + /** + * Gets the SQL Statements to update given schema with the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return array + */ + function getUpdateSchema(Schema $toSchema, $noDrops = false); + + /** + * Gets the SQL Statements to drop the given schema from underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return array + */ + function getDropSchema(Schema $dropSchema); + + /** + * Gets the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + function getDropAllSchema(); + + /** + * Creates the Schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return void + */ + function createSchema(Schema $createSchema); + + /** + * Updates the Schema to new schema version. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return void + */ + function updateSchema(Schema $toSchema, $noDrops = false); + + /** + * Drops the given database schema from the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return void + */ + function dropSchema(Schema $dropSchema); + + /** + * Drops all assets from the underlying db. + * + * @return void + */ + function dropAllSchema(); +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php new file mode 100644 index 000000000..7cab7a5b3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -0,0 +1,176 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; + +/** + * Schema Synchronizer for Default DBAL Connection. + * + * @author Benjamin Eberlei + */ +class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + parent::__construct($conn); + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + return $createSchema->toSql($this->platform); + } + + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + $comparator = new Comparator(); + $sm = $this->conn->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($noDrops) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $sm = $this->conn->getSchemaManager(); + + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ($dropSchema->hasTable($table->getName())) { + $visitor->acceptTable($table); + } + + foreach ($table->getForeignKeys() as $foreignKey) { + if ( ! $dropSchema->hasTable($table->getName())) { + continue; + } + + if ( ! $dropSchema->hasTable($foreignKey->getForeignTableName())) { + continue; + } + + $visitor->acceptForeignKey($table, $foreignKey); + } + } + + if ( ! $this->platform->supportsSequences()) { + return $visitor->getQueries(); + } + + foreach ($dropSchema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($dropSchema->getTables() as $table) { + if ( ! $table->hasPrimaryKey()) { + continue; + } + + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) > 1) { + continue; + } + + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $sm = $this->conn->getSchemaManager(); + $visitor = new DropSchemaSqlCollector($this->platform); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $sm->createSchema(); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSql($this->getDropAllSchema()); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Table.php b/wbce/include/Doctrine/DBAL/Schema/Table.php new file mode 100644 index 000000000..428761ea3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,863 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\DBALException; + +/** + * Object Representation of a table. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var Column[] + */ + protected $_columns = array(); + + /** + * @var Index[] + */ + private $implicitIndexes = array(); + + /** + * @var Index[] + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var ForeignKeyConstraint[] + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * @param string $tableName + * @param Column[] $columns + * @param Index[] $indexes + * @param ForeignKeyConstraint[] $fkConstraints + * @param integer $idGeneratorType + * @param array $options + * + * @throws DBALException + */ + public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array()) + { + if (strlen($tableName) == 0) { + throw DBALException::invalidTableName($tableName); + } + + $this->_setName($tableName); + + foreach ($columns as $column) { + $this->_addColumn($column); + } + + foreach ($indexes as $idx) { + $this->_addIndex($idx); + } + + foreach ($fkConstraints as $constraint) { + $this->_addForeignKeyConstraint($constraint); + } + + $this->_options = $options; + } + + /** + * @param SchemaConfig $schemaConfig + * + * @return void + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return integer + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Sets the Primary Key. + * + * @param array $columns + * @param string|boolean $indexName + * + * @return self + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $this->_addIndex($this->_createIndex($columns, $indexName ?: "primary", true, true)); + + foreach ($columns as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $this; + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $flags + * @param array $options + * + * @return self + */ + public function addIndex(array $columnNames, $indexName = null, array $flags = array(), array $options = array()) + { + if ($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); + } + + /** + * Drops the primary key from this table. + * + * @return void + */ + public function dropPrimaryKey() + { + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = false; + } + + /** + * Drops an index from this table. + * + * @param string $indexName The index name. + * + * @return void + * + * @throws SchemaException If the index does not exist. + */ + public function dropIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + unset($this->_indexes[$indexName]); + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $options + * + * @return self + */ + public function addUniqueIndex(array $columnNames, $indexName = null, array $options = array()) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, array(), $options)); + } + + /** + * Renames an index. + * + * @param string $oldIndexName The name of the index to rename from. + * @param string|null $newIndexName The name of the index to rename to. + * If null is given, the index name will be auto-generated. + * + * @return self This table instance. + * + * @throws SchemaException if no index exists for the given current name + * or if an index with the given new name already exists on this table. + */ + public function renameIndex($oldIndexName, $newIndexName = null) + { + $oldIndexName = $this->normalizeIdentifier($oldIndexName); + $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName); + + if ($oldIndexName === $normalizedNewIndexName) { + return $this; + } + + if ( ! $this->hasIndex($oldIndexName)) { + throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name); + } + + if ($this->hasIndex($normalizedNewIndexName)) { + throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name); + } + + $oldIndex = $this->_indexes[$oldIndexName]; + + if ($oldIndex->isPrimary()) { + $this->dropPrimaryKey(); + + return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName); + } + + unset($this->_indexes[$oldIndexName]); + + if ($oldIndex->isUnique()) { + return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions()); + } + + return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions()); + } + + /** + * Checks if an index begins in the order of the given columns. + * + * @param array $columnsNames + * + * @return boolean + */ + public function columnsAreIndexed(array $columnsNames) + { + foreach ($this->getIndexes() as $index) { + /* @var $index Index */ + if ($index->spansColumns($columnsNames)) { + return true; + } + } + + return false; + } + + /** + * @param array $columnNames + * @param string $indexName + * @param boolean $isUnique + * @param boolean $isPrimary + * @param array $flags + * @param array $options + * + * @return Index + * + * @throws SchemaException + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array(), array $options = array()) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); + } + + /** + * @param string $columnName + * @param string $typeName + * @param array $options + * + * @return Column + */ + public function addColumn($columnName, $typeName, array $options=array()) + { + $column = new Column($columnName, Type::getType($typeName), $options); + + $this->_addColumn($column); + + return $column; + } + + /** + * Renames a Column. + * + * @param string $oldColumnName + * @param string $newColumnName + * + * @deprecated + * + * @throws DBALException + */ + public function renameColumn($oldColumnName, $newColumnName) + { + throw new DBALException("Table#renameColumn() was removed, because it drops and recreates " . + "the column instead. There is no fix available, because a schema diff cannot reliably detect if a " . + "column was renamed or one column was created and another one dropped."); + } + + /** + * Change Column Details. + * + * @param string $columnName + * @param array $options + * + * @return self + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + + return $this; + } + + /** + * Drops a Column from the Table. + * + * @param string $columnName + * + * @return self + */ + public function dropColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + unset($this->_columns[$columnName]); + + return $this; + } + + /** + * Adds a foreign key constraint. + * + * Name is inferred from the local columns. + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * @param string|null $constraintName + * + * @return self + */ + public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array(), $constraintName = null) + { + $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength()); + + return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Adds a foreign key constraint. + * + * Name is to be generated by the database itself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Adds a foreign key constraint with a given name. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param string $name + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + * + * @throws SchemaException + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + foreach ($foreignColumnNames as $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } + + foreach ($localColumnNames as $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTable, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * + * @return self + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + + return $this; + } + + /** + * @param Column $column + * + * @return void + * + * @throws SchemaException + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Adds an index to the table. + * + * @param Index $indexCandidate + * + * @return self + * + * @throws SchemaException + */ + protected function _addIndex(Index $indexCandidate) + { + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = array(); + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if ($implicitIndex->isFullfilledBy($indexCandidate) && isset($this->_indexes[$name])) { + $replacedImplicitIndexes[] = $name; + } + } + + if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName != false && $indexCandidate->isPrimary()) + ) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + * + * @return void + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + if (strlen($constraint->getName())) { + $name = $constraint->getName(); + } else { + $name = $this->_generateIdentifierName( + array_merge((array) $this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength() + ); + } + $name = $this->normalizeIdentifier($name); + + $this->_fkConstraints[$name] = $constraint; + + // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request. + // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes + // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns). + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $constraint->getColumns()), + "idx", + $this->_getMaxIdentifierLength() + ); + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return; + } + } + + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + } + + /** + * Returns whether this table has a foreign key constraint with the given name. + * + * @param string $constraintName + * + * @return boolean + */ + public function hasForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * Returns the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return ForeignKeyConstraint + * + * @throws SchemaException If the foreign key does not exist. + */ + public function getForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + if (!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + /** + * Removes the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return void + * + * @throws SchemaException + */ + public function removeForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + if (!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + unset($this->_fkConstraints[$constraintName]); + } + + /** + * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) + * @return Column[] + */ + public function getColumns() + { + $primaryKeyColumns = []; + if ($this->hasPrimaryKey()) { + $primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns()); + } + + return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns); + } + + /** + * Returns foreign key columns + * @return Column[] + */ + private function getForeignKeyColumns() + { + $foreignKeyColumns = []; + foreach ($this->getForeignKeys() as $foreignKey) { + /* @var $foreignKey ForeignKeyConstraint */ + $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns()); + } + return $this->filterColumns($foreignKeyColumns); + } + + /** + * Returns only columns that have specified names + * @param array $columnNames + * @return Column[] + */ + private function filterColumns(array $columnNames) + { + return array_filter($this->_columns, function ($columnName) use ($columnNames) { + return in_array($columnName, $columnNames, true); + }, ARRAY_FILTER_USE_KEY); + } + + /** + * Returns whether this table has a Column with the given name. + * + * @param string $columnName The column name. + * + * @return boolean + */ + public function hasColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + + return isset($this->_columns[$columnName]); + } + + /** + * Returns the Column with the given name. + * + * @param string $columnName The column name. + * + * @return Column + * + * @throws SchemaException If the column does not exist. + */ + public function getColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * Returns the primary key. + * + * @return Index|null The primary key, or null if this Table has no primary key. + */ + public function getPrimaryKey() + { + if ( ! $this->hasPrimaryKey()) { + return null; + } + + return $this->getIndex($this->_primaryKeyName); + } + + /** + * Returns the primary key columns. + * + * @return array + * + * @throws DBALException + */ + public function getPrimaryKeyColumns() + { + if ( ! $this->hasPrimaryKey()) { + throw new DBALException("Table " . $this->getName() . " has no primary key."); + } + return $this->getPrimaryKey()->getColumns(); + } + + /** + * Returns whether this table has a primary key. + * + * @return boolean + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * Returns whether this table has an Index with the given name. + * + * @param string $indexName The index name. + * + * @return boolean + */ + public function hasIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + + return (isset($this->_indexes[$indexName])); + } + + /** + * Returns the Index with the given name. + * + * @param string $indexName The index name. + * + * @return Index + * + * @throws SchemaException If the index does not exist. + */ + public function getIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + + return $this->_indexes[$indexName]; + } + + /** + * @return Index[] + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Returns the foreign key constraints. + * + * @return ForeignKeyConstraint[] + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptTable($this); + + foreach ($this->getColumns() as $column) { + $visitor->acceptColumn($this, $column); + } + + foreach ($this->getIndexes() as $index) { + $visitor->acceptIndex($this, $index); + } + + foreach ($this->getForeignKeys() as $constraint) { + $visitor->acceptForeignKey($this, $constraint); + } + } + + /** + * Clone of a Table triggers a deep clone of all affected assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_columns as $k => $column) { + $this->_columns[$k] = clone $column; + } + foreach ($this->_indexes as $k => $index) { + $this->_indexes[$k] = clone $index; + } + foreach ($this->_fkConstraints as $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; + $this->_fkConstraints[$k]->setLocalTable($this); + } + } + + /** + * Normalizes a given identifier. + * + * Trims quotes and lowercases the given identifier. + * + * @param string $identifier The identifier to normalize. + * + * @return string The normalized identifier. + */ + private function normalizeIdentifier($identifier) + { + return $this->trimQuotes(strtolower($identifier)); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/TableDiff.php b/wbce/include/Doctrine/DBAL/Schema/TableDiff.php new file mode 100644 index 000000000..a837fab67 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,170 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Table Diff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string|boolean + */ + public $newName = false; + + /** + * All added fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $addedColumns; + + /** + * All changed fields. + * + * @var \Doctrine\DBAL\Schema\ColumnDiff[] + */ + public $changedColumns = array(); + + /** + * All removed fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $renamedColumns = array(); + + /** + * All added indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $addedIndexes = array(); + + /** + * All changed indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $removedIndexes = array(); + + /** + * Indexes that are only renamed but are identical otherwise. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $renamedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $removedForeignKeys = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + public $fromTable; + + /** + * Constructs an TableDiff object. + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column[] $addedColumns + * @param \Doctrine\DBAL\Schema\ColumnDiff[] $changedColumns + * @param \Doctrine\DBAL\Schema\Column[] $removedColumns + * @param \Doctrine\DBAL\Schema\Index[] $addedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $changedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $removedIndexes + * @param \Doctrine\DBAL\Schema\Table|null $fromTable + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array(), Table $fromTable = null) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + $this->fromTable = $fromTable; + } + + /** + * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. + * + * @return \Doctrine\DBAL\Schema\Identifier + */ + public function getName(AbstractPlatform $platform) + { + return new Identifier( + $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name + ); + } + + /** + * @return \Doctrine\DBAL\Schema\Identifier|boolean + */ + public function getNewName() + { + return $this->newName ? new Identifier($this->newName) : $this->newName; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/View.php b/wbce/include/Doctrine/DBAL/Schema/View.php new file mode 100644 index 000000000..0ef7d305b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/View.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Representation of a Database View. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_sql; + + /** + * @param string $name + * @param string $sql + */ + public function __construct($name, $sql) + { + $this->_setName($name); + $this->_sql = $sql; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php new file mode 100644 index 000000000..7bf5b2fec --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Abstract Visitor with empty methods for easy extension. + */ +class AbstractVisitor implements Visitor, NamespaceVisitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php new file mode 100644 index 000000000..ba196d0b4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +class CreateSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var array + */ + private $createNamespaceQueries = array(); + + /** + * @var array + */ + private $createTableQueries = array(); + + /** + * @var array + */ + private $createSequenceQueries = array(); + + /** + * @var array + */ + private $createFkConstraintQueries = array(); + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform = null; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + if ($this->platform->supportsSchemas()) { + $this->createNamespaceQueries[] = $this->platform->getCreateSchemaSQL($namespaceName); + } + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table)); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if ($this->platform->supportsForeignKeyConstraints()) { + $this->createFkConstraintQueries[] = $this->platform->getCreateForeignKeySQL($fkConstraint, $localTable); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + $this->createSequenceQueries[] = $this->platform->getCreateSequenceSQL($sequence); + } + + /** + * @return void + */ + public function resetQueries() + { + $this->createNamespaceQueries = array(); + $this->createTableQueries = array(); + $this->createSequenceQueries = array(); + $this->createFkConstraintQueries = array(); + } + + /** + * Gets all queries collected so far. + * + * @return array + */ + public function getQueries() + { + return array_merge( + $this->createNamespaceQueries, + $this->createTableQueries, + $this->createSequenceQueries, + $this->createFkConstraintQueries + ); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php new file mode 100644 index 000000000..ab9752d58 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\SchemaException; + +/** + * Gathers SQL statements that allow to completely drop the current schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * @var AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint, $localTable); + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + $this->sequences->attach($sequence); + } + + /** + * @return void + */ + public function clearQueries() + { + $this->constraints = new \SplObjectStorage(); + $this->sequences = new \SplObjectStorage(); + $this->tables = new \SplObjectStorage(); + } + + /** + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach ($this->constraints as $fkConstraint) { + $localTable = $this->constraints[$fkConstraint]; + $sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable); + } + + foreach ($this->sequences as $sequence) { + $sql[] = $this->platform->getDropSequenceSQL($sequence); + } + + foreach ($this->tables as $table) { + $sql[] = $this->platform->getDropTableSQL($table); + } + + return $sql; + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/Graphviz.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/Graphviz.php new file mode 100644 index 000000000..1573ddf5b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; + +/** + * Create a Graphviz output of a Schema. + */ +class Graphviz extends AbstractVisitor +{ + /** + * @var string + */ + private $output = ''; + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $this->output .= $this->createNodeRelation( + $fkConstraint->getLocalTableName() . ":col" . current($fkConstraint->getLocalColumns()).":se", + $fkConstraint->getForeignTableName() . ":col" . current($fkConstraint->getForeignColumns()).":se", + array( + 'dir' => 'back', + 'arrowtail' => 'dot', + 'arrowhead' => 'normal', + ) + ); + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $this->output = 'digraph "' . sha1(mt_rand()) . '" {' . "\n"; + $this->output .= 'splines = true;' . "\n"; + $this->output .= 'overlap = false;' . "\n"; + $this->output .= 'outputorder=edgesfirst;'."\n"; + $this->output .= 'mindist = 0.6;' . "\n"; + $this->output .= 'sep = .2;' . "\n"; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel($table), + 'shape' => 'plaintext', + ) + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return string + */ + private function createTableLabel(Table $table) + { + // Start the table + $label = '<'; + + // The title + $label .= ''; + + // The attributes block + foreach ($table->getColumns() as $column) { + $columnLabel = $column->getName(); + + $label .= ''; + $label .= ''; + $label .= ''; + } + + // End the table + $label .= '
' . $table->getName() . '
'; + $label .= '' . $columnLabel . ''; + $label .= '' . strtolower($column->getType()) . ''; + if ($table->hasPrimaryKey() && in_array($column->getName(), $table->getPrimaryKey()->getColumns())) { + $label .= "\xe2\x9c\xb7"; + } + $label .= '
>'; + + return $label; + } + + /** + * @param string $name + * @param array $options + * + * @return string + */ + private function createNode($name, $options) + { + $node = $name . " ["; + foreach ($options as $key => $value) { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + + return $node; + } + + /** + * @param string $node1 + * @param string $node2 + * @param array $options + * + * @return string + */ + private function createNodeRelation($node1, $node2, $options) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach ($options as $key => $value) { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + + return $relation; + } + + /** + * Get Graphviz Output + * + * @return string + */ + public function getOutput() + { + return $this->output . "}"; + } + + /** + * Writes dot language output to a file. This should usually be a *.dot file. + * + * You have to convert the output into a viewable format. For example use "neato" on linux systems + * and execute: + * + * neato -Tpng -o er.png er.dot + * + * @param string $filename + * + * @return void + */ + public function write($filename) + { + file_put_contents($filename, $this->getOutput()); + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php new file mode 100644 index 000000000..da7b1edbc --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +/** + * Visitor that can visit schema namespaces. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface NamespaceVisitor +{ + /** + * Accepts a schema namespace name. + * + * @param string $namespaceName The schema namespace name to accept. + */ + public function acceptNamespace($namespaceName); +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php new file mode 100644 index 000000000..e328d071c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Removes assets from a schema that are not in the default namespace. + * + * Some databases such as MySQL support cross databases joins, but don't + * allow to call DDLs to a database from another connected database. + * Before a schema is serialized into SQL this visitor can cleanup schemas with + * non default namespaces. + * + * This visitor filters all these non-default namespaced tables and sequences + * and removes them from the SChema instance. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class RemoveNamespacedAssets extends AbstractVisitor +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropTable($table->getName()); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + // The table may already be deleted in a previous + // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that + // point to nowhere. + if ( ! $this->schema->hasTable($fkConstraint->getForeignTableName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + return; + } + + $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); + if ( ! $foreignTable->isInDefaultNamespace($this->schema->getName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php new file mode 100644 index 000000000..bde4453b3 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Visit a SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Benjamin Eberlei + */ +interface SchemaDiffVisitor +{ + /** + * Visit an orphaned foreign key whose table was deleted. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitOrphanedForeignKey(ForeignKeyConstraint $foreignKey); + + /** + * Visit a sequence that has changed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitChangedSequence(Sequence $sequence); + + /** + * Visit a sequence that has been removed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitRemovedSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitNewSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitNewTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitNewTableForeignKey(Table $table, ForeignKeyConstraint $foreignKey); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitRemovedTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + */ + function visitChangedTable(TableDiff $tableDiff); +} diff --git a/wbce/include/Doctrine/DBAL/Schema/Visitor/Visitor.php b/wbce/include/Doctrine/DBAL/Schema/Visitor/Visitor.php new file mode 100644 index 000000000..656f0e20c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return void + */ + public function acceptSchema(Schema $schema); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function acceptTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return void + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + * + * @return void + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return void + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + */ + public function acceptSequence(Sequence $sequence); +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/PoolingShardConnection.php b/wbce/include/Doctrine/DBAL/Sharding/PoolingShardConnection.php new file mode 100644 index 000000000..8b9d9210e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -0,0 +1,272 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Sharding implementation that pools many different connections + * internally and serves data from the currently active connection. + * + * The internals of this class are: + * + * - All sharding clients are specified and given a shard-id during + * configuration. + * - By default, the global shard is selected. If no global shard is configured + * an exception is thrown on access. + * - Selecting a shard by distribution value delegates the mapping + * "distributionValue" => "client" to the ShardChooser interface. + * - An exception is thrown if trying to switch shards during an open + * transaction. + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection', + * 'driver' => 'pdo_mysql', + * 'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'shards' => array( + * array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ), + * 'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser', + * )); + * $shardManager = $conn->getShardManager(); + * $shardManager->selectGlobal(); + * $shardManager->selectShard($value); + * + * @author Benjamin Eberlei + */ +class PoolingShardConnection extends Connection +{ + /** + * @var array + */ + private $activeConnections; + + /** + * @var integer + */ + private $activeShardId; + + /** + * @var array + */ + private $connections; + + /** + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + * + * @throws \InvalidArgumentException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['global']) || !isset($params['shards'])) { + throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations."); + } + + if ( !isset($params['shardChoser'])) { + throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'"); + } + + if (is_string($params['shardChoser'])) { + $params['shardChoser'] = new $params['shardChoser']; + } + + if ( ! ($params['shardChoser'] instanceof ShardChoser)) { + throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser"); + } + + $this->connections[0] = array_merge($params, $params['global']); + + foreach ($params['shards'] as $shard) { + if ( ! isset($shard['id'])) { + throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specify a unique shard-id."); + } + + if ( !is_numeric($shard['id']) || $shard['id'] < 1) { + throw new \InvalidArgumentException("Shard Id has to be a non-negative number."); + } + + if (isset($this->connections[$shard['id']])) { + throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration."); + } + + $this->connections[$shard['id']] = array_merge($params, $shard); + } + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Get active shard id. + * + * @return integer + */ + public function getActiveShardId() + { + return $this->activeShardId; + } + + /** + * {@inheritdoc} + */ + public function getParams() + { + return $this->activeShardId ? $this->connections[$this->activeShardId] : $this->connections[0]; + } + + /** + * {@inheritdoc} + */ + public function getHost() + { + $params = $this->getParams(); + + return isset($params['host']) ? $params['host'] : parent::getHost(); + } + + /** + * {@inheritdoc} + */ + public function getPort() + { + $params = $this->getParams(); + + return isset($params['port']) ? $params['port'] : parent::getPort(); + } + + /** + * {@inheritdoc} + */ + public function getUsername() + { + $params = $this->getParams(); + + return isset($params['user']) ? $params['user'] : parent::getUsername(); + } + + /** + * {@inheritdoc} + */ + public function getPassword() + { + $params = $this->getParams(); + + return isset($params['password']) ? $params['password'] : parent::getPassword(); + } + + /** + * Connects to a given shard. + * + * @param mixed $shardId + * + * @return boolean + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function connect($shardId = null) + { + if ($shardId === null && $this->_conn) { + return false; + } + + if ($shardId !== null && $shardId === $this->activeShardId) { + return false; + } + + if ($this->getTransactionNestingLevel() > 0) { + throw new ShardingException("Cannot switch shard when transaction is active."); + } + + $this->activeShardId = (int)$shardId; + + if (isset($this->activeConnections[$this->activeShardId])) { + $this->_conn = $this->activeConnections[$this->activeShardId]; + + return false; + } + + $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId); + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to a specific connection. + * + * @param string $shardId + * + * @return \Doctrine\DBAL\Driver\Connection + */ + protected function connectTo($shardId) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : []; + + $connectionParams = $this->connections[$shardId]; + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string|null $shardId + * + * @return boolean + */ + public function isConnected($shardId = null) + { + if ($shardId === null) { + return $this->_conn !== null; + } + + return isset($this->activeConnections[$shardId]); + } + + /** + * @return void + */ + public function close() + { + $this->_conn = null; + $this->activeConnections = null; + $this->activeShardId = null; + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/PoolingShardManager.php b/wbce/include/Doctrine/DBAL/Sharding/PoolingShardManager.php new file mode 100644 index 000000000..798e3c493 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Shard Manager for the Connection Pooling Shard Strategy + * + * @author Benjamin Eberlei + */ +class PoolingShardManager implements ShardManager +{ + /** + * @var PoolingShardConnection + */ + private $conn; + + /** + * @var ShardChoser + */ + private $choser; + + /** + * @var string|null + */ + private $currentDistributionValue; + + /** + * @param PoolingShardConnection $conn + */ + public function __construct(PoolingShardConnection $conn) + { + $params = $conn->getParams(); + $this->conn = $conn; + $this->choser = $params['shardChoser']; + } + + /** + * @return void + */ + public function selectGlobal() + { + $this->conn->connect(0); + $this->currentDistributionValue = null; + } + + /** + * @param string $distributionValue + * + * @return void + */ + public function selectShard($distributionValue) + { + $shardId = $this->choser->pickShard($distributionValue, $this->conn); + $this->conn->connect($shardId); + $this->currentDistributionValue = $distributionValue; + } + + /** + * @return string|null + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * @return array + */ + public function getShards() + { + $params = $this->conn->getParams(); + $shards = []; + + foreach ($params['shards'] as $shard) { + $shards[] = ['id' => $shard['id']]; + } + + return $shards; + } + + /** + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + * + * @throws \RuntimeException + */ + public function queryAll($sql, array $params, array $types) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found."); + } + + $result = []; + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->conn->connect($shard['id']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php new file mode 100644 index 000000000..b7830fa3c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -0,0 +1,298 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer; + +/** + * SQL Azure Schema Synchronizer. + * + * Will iterate over all shards when performing schema operations. This is done + * by partitioning the passed schema into subschemas for the federation and the + * global database and then applying the operations step by step using the + * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}. + * + * @author Benjamin Eberlei + */ +class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer +{ + const FEDERATION_TABLE_FEDERATED = 'azure.federated'; + const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName'; + + /** + * @var \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager + */ + private $shardManager; + + /** + * @var \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer + */ + private $synchronizer; + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager $shardManager + * @param \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer|null $sync + */ + public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null) + { + parent::__construct($conn); + $this->shardManager = $shardManager; + $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + $sql = []; + + list($global, $federation) = $this->partitionSchema($createSchema); + + $globalSql = $this->synchronizer->getCreateSchema($global); + if ($globalSql) { + $sql[] = "-- Create Root Federation\n" . + "USE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $federationSql = $this->synchronizer->getCreateSchema($federation); + + if ($federationSql) { + $defaultValue = $this->getFederationTypeDefaultValue(); + + $sql[] = $this->getCreateFederationStatement(); + $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + return $this->work($toSchema, function ($synchronizer, $schema) use ($noDrops) { + return $synchronizer->getUpdateSchema($schema, $noDrops); + }); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + return $this->work($dropSchema, function ($synchronizer, $schema) { + return $synchronizer->getDropSchema($schema); + }); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $this->shardManager->selectGlobal(); + $globalSql = $this->synchronizer->getDropAllSchema(); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $this->synchronizer->getDropAllSchema(); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + $sql[] = "USE FEDERATION ROOT WITH RESET;"; + $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName(); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSqlSafely($this->getDropAllSchema()); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return array + */ + private function partitionSchema(Schema $schema) + { + return array( + $this->extractSchemaFederation($schema, false), + $this->extractSchemaFederation($schema, true), + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param boolean $isFederation + * + * @return \Doctrine\DBAL\Schema\Schema + * + * @throws \RuntimeException + */ + private function extractSchemaFederation(Schema $schema, $isFederation) + { + $partitionedSchema = clone $schema; + + foreach ($partitionedSchema->getTables() as $table) { + if ($isFederation) { + $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); + } + + if ($table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + $partitionedSchema->dropTable($table->getName()); + } else { + foreach ($table->getForeignKeys() as $fk) { + $foreignTable = $schema->getTable($fk->getForeignTableName()); + if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + throw new \RuntimeException("Cannot have foreign key between global/federation."); + } + } + } + } + + return $partitionedSchema; + } + + /** + * Work on the Global/Federation based on currently existing shards and + * perform the given operation on the underlying schema synchronizer given + * the different partitioned schema instances. + * + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Closure $operation + * + * @return array + */ + private function work(Schema $schema, \Closure $operation) + { + list($global, $federation) = $this->partitionSchema($schema); + $sql = []; + + $this->shardManager->selectGlobal(); + $globalSql = $operation($this->synchronizer, $global); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $operation($this->synchronizer, $federation); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + return $sql; + } + + /** + * @return string + */ + private function getFederationTypeDefaultValue() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + + switch ($federationType->getName()) { + case Type::GUID: + $defaultValue = '00000000-0000-0000-0000-000000000000'; + break; + case Type::INTEGER: + case Type::SMALLINT: + case Type::BIGINT: + $defaultValue = '0'; + break; + default: + $defaultValue = ''; + break; + } + + return $defaultValue; + } + + /** + * @return string + */ + private function getCreateFederationStatement() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + $federationTypeSql = $federationType->getSQLDeclaration([], $this->conn->getDatabasePlatform()); + + return "--Create Federation\n" . + "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ." RANGE)"; + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php new file mode 100644 index 000000000..028b50974 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Sharding\ShardManager; +use Doctrine\DBAL\Sharding\ShardingException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Sharding using the SQL Azure Federations support. + * + * @author Benjamin Eberlei + */ +class SQLAzureShardManager implements ShardManager +{ + /** + * @var string + */ + private $federationName; + + /** + * @var boolean + */ + private $filteringEnabled; + + /** + * @var string + */ + private $distributionKey; + + /** + * @var string + */ + private $distributionType; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $currentDistributionValue; + + /** + * @param \Doctrine\DBAL\Connection $conn + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + $params = $conn->getParams(); + + if ( ! isset($params['sharding']['federationName'])) { + throw ShardingException::missingDefaultFederationName(); + } + + if ( ! isset($params['sharding']['distributionKey'])) { + throw ShardingException::missingDefaultDistributionKey(); + } + + if ( ! isset($params['sharding']['distributionType'])) { + throw ShardingException::missingDistributionType(); + } + + $this->federationName = $params['sharding']['federationName']; + $this->distributionKey = $params['sharding']['distributionKey']; + $this->distributionType = $params['sharding']['distributionType']; + $this->filteringEnabled = (isset($params['sharding']['filteringEnabled'])) ? (bool) $params['sharding']['filteringEnabled'] : false; + } + + /** + * Gets the name of the federation. + * + * @return string + */ + public function getFederationName() + { + return $this->federationName; + } + + /** + * Gets the distribution key. + * + * @return string + */ + public function getDistributionKey() + { + return $this->distributionKey; + } + + /** + * Gets the Doctrine Type name used for the distribution. + * + * @return string + */ + public function getDistributionType() + { + return $this->distributionType; + } + + /** + * Sets Enabled/Disable filtering on the fly. + * + * @param boolean $flag + * + * @return void + */ + public function setFilteringEnabled($flag) + { + $this->filteringEnabled = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function selectGlobal() + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + $sql = "USE FEDERATION ROOT WITH RESET"; + $this->conn->exec($sql); + $this->currentDistributionValue = null; + } + + /** + * {@inheritDoc} + */ + public function selectShard($distributionValue) + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + if ($distributionValue === null || is_bool($distributionValue) || !is_scalar($distributionValue)) { + throw ShardingException::noShardDistributionValue(); + } + + $platform = $this->conn->getDatabasePlatform(); + $sql = sprintf( + "USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;", + $platform->quoteIdentifier($this->federationName), + $platform->quoteIdentifier($this->distributionKey), + $this->conn->quote($distributionValue), + ($this->filteringEnabled ? 'ON' : 'OFF') + ); + + $this->conn->exec($sql); + $this->currentDistributionValue = $distributionValue; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * {@inheritDoc} + */ + public function getShards() + { + $sql = "SELECT member_id as id, + distribution_name as distribution_key, + CAST(range_low AS CHAR) AS rangeLow, + CAST(range_high AS CHAR) AS rangeHigh + FROM sys.federation_member_distributions d + INNER JOIN sys.federations f ON f.federation_id = d.federation_id + WHERE f.name = " . $this->conn->quote($this->federationName); + + return $this->conn->fetchAll($sql); + } + + /** + * {@inheritDoc} + */ + public function queryAll($sql, array $params = [], array $types = []) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found for " . $this->federationName); + } + + $result = []; + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->selectShard($shard['rangeLow']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } + + /** + * Splits Federation at a given distribution value. + * + * @param mixed $splitDistributionValue + * + * @return void + */ + public function splitFederation($splitDistributionValue) + { + $type = Type::getType($this->distributionType); + + $sql = "ALTER FEDERATION " . $this->getFederationName() . " " . + "SPLIT AT (" . $this->getDistributionKey() . " = " . + $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ")"; + $this->conn->exec($sql); + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php new file mode 100644 index 000000000..7e45cb756 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Converts a single tenant schema into a multi-tenant schema for SQL Azure + * Federations under the following assumptions: + * + * - Every table is part of the multi-tenant application, only explicitly + * excluded tables are non-federated. The behavior of the tables being in + * global or federated database is undefined. It depends on you selecting a + * federation before DDL statements or not. + * - Every Primary key of a federated table is extended by another column + * 'tenant_id' with a default value of the SQLAzure function + * `federation_filtering_value('tenant_id')`. + * - You always have to work with `filtering=On` when using federations with this + * multi-tenant approach. + * - Primary keys are either using globally unique ids (GUID, Table Generator) + * or you explicitly add the tenant_id in every UPDATE or DELETE statement + * (otherwise they will affect the same-id rows from other tenants as well). + * SQLAzure throws errors when you try to create IDENTIY columns on federated + * tables. + * + * @author Benjamin Eberlei + */ +class MultiTenantVisitor implements Visitor +{ + /** + * @var array + */ + private $excludedTables = array(); + + /** + * @var string + */ + private $tenantColumnName; + + /** + * @var string + */ + private $tenantColumnType = 'integer'; + + /** + * Name of the federation distribution, defaulting to the tenantColumnName + * if not specified. + * + * @var string + */ + private $distributionName; + + /** + * @param array $excludedTables + * @param string $tenantColumnName + * @param string|null $distributionName + */ + public function __construct(array $excludedTables = [], $tenantColumnName = 'tenant_id', $distributionName = null) + { + $this->excludedTables = $excludedTables; + $this->tenantColumnName = $tenantColumnName; + $this->distributionName = $distributionName ?: $tenantColumnName; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if (in_array($table->getName(), $this->excludedTables)) { + return; + } + + $table->addColumn($this->tenantColumnName, $this->tenantColumnType, [ + 'default' => "federation_filtering_value('". $this->distributionName ."')", + ]); + + $clusteredIndex = $this->getClusteredIndex($table); + + $indexColumns = $clusteredIndex->getColumns(); + $indexColumns[] = $this->tenantColumnName; + + if ($clusteredIndex->isPrimary()) { + $table->dropPrimaryKey(); + $table->setPrimaryKey($indexColumns); + } else { + $table->dropIndex($clusteredIndex->getName()); + $table->addIndex($indexColumns, $clusteredIndex->getName()); + $table->getIndex($clusteredIndex->getName())->addFlag('clustered'); + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return \Doctrine\DBAL\Schema\Index + * + * @throws \RuntimeException + */ + private function getClusteredIndex($table) + { + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { + return $index; + } elseif ($index->hasFlag('clustered')) { + return $index; + } + } + throw new \RuntimeException("No clustered index found on table " . $table->getName()); + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php new file mode 100644 index 000000000..f1d51b8c9 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * The MultiTenant Shard choser assumes that the distribution value directly + * maps to the shard id. + * + * @author Benjamin Eberlei + */ +class MultiTenantShardChoser implements ShardChoser +{ + /** + * {@inheritdoc} + */ + public function pickShard($distributionValue, PoolingShardConnection $conn) + { + return $distributionValue; + } +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php b/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php new file mode 100644 index 000000000..a7b85a66b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * Given a distribution value this shard-choser strategy will pick the shard to + * connect to for retrieving rows with the distribution value. + * + * @author Benjamin Eberlei + */ +interface ShardChoser +{ + /** + * Picks a shard for the given distribution value. + * + * @param string $distributionValue + * @param \Doctrine\DBAL\Sharding\PoolingShardConnection $conn + * + * @return integer + */ + function pickShard($distributionValue, PoolingShardConnection $conn); +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/ShardManager.php b/wbce/include/Doctrine/DBAL/Sharding/ShardManager.php new file mode 100644 index 000000000..b742793b8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/ShardManager.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +/** + * Sharding Manager gives access to APIs to implementing sharding on top of + * Doctrine\DBAL\Connection instances. + * + * For simplicity and developer ease-of-use (and understanding) the sharding + * API only covers single shard queries, no fan-out support. It is primarily + * suited for multi-tenant applications. + * + * The assumption about sharding here + * is that a distribution value can be found that gives access to all the + * necessary data for all use-cases. Switching between shards should be done with + * caution, especially if lazy loading is implemented. Any query is always + * executed against the last shard that was selected. If a query is created for + * a shard Y but then a shard X is selected when its actually executed you + * will hit the wrong shard. + * + * @author Benjamin Eberlei + */ +interface ShardManager +{ + /** + * Selects global database with global data. + * + * This is the default database that is connected when no shard is + * selected. + * + * @return void + */ + function selectGlobal(); + + /** + * Selects the shard against which the queries after this statement will be issued. + * + * @param string $distributionValue + * + * @return void + * + * @throws \Doctrine\DBAL\Sharding\ShardingException If no value is passed as shard identifier. + */ + function selectShard($distributionValue); + + /** + * Gets the distribution value currently used for sharding. + * + * @return string + */ + function getCurrentDistributionValue(); + + /** + * Gets information about the amount of shards and other details. + * + * Format is implementation specific, each shard is one element and has an + * 'id' attribute at least. + * + * @return array + */ + function getShards(); + + /** + * Queries all shards in undefined order and return the results appended to + * each other. Restore the previous distribution value after execution. + * + * Using {@link \Doctrine\DBAL\Connection::fetchAll} to retrieve rows internally. + * + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + */ + function queryAll($sql, array $params, array $types); +} diff --git a/wbce/include/Doctrine/DBAL/Sharding/ShardingException.php b/wbce/include/Doctrine/DBAL/Sharding/ShardingException.php new file mode 100644 index 000000000..407bf847d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Sharding/ShardingException.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\DBALException; + +/** + * Sharding related Exceptions + * + * @since 2.3 + */ +class ShardingException extends DBALException +{ + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function notImplemented() + { + return new self("This functionality is not implemented with this sharding provider.", 1331557937); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultFederationName() + { + return new self("SQLAzure requires a federation name to be set during sharding configuration.", 1332141280); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultDistributionKey() + { + return new self("SQLAzure requires a distribution key to be set during sharding configuration.", 1332141329); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function activeTransaction() + { + return new self("Cannot switch shard during an active transaction.", 1332141766); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function noShardDistributionValue() + { + return new self("You have to specify a string or integer as shard distribution value.", 1332142103); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDistributionType() + { + return new self("You have to specify a sharding distribution type such as 'integer', 'string', 'guid'."); + } +} diff --git a/wbce/include/Doctrine/DBAL/Statement.php b/wbce/include/Doctrine/DBAL/Statement.php new file mode 100644 index 000000000..88038dc61 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Statement.php @@ -0,0 +1,305 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Driver\Statement as DriverStatement; + +/** + * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support + * for logging, DBAL mapping types, etc. + * + * @author Roman Borschel + * @since 2.0 + */ +class Statement implements \IteratorAggregate, DriverStatement +{ + /** + * The SQL statement. + * + * @var string + */ + protected $sql; + + /** + * The bound parameters. + * + * @var array + */ + protected $params = array(); + + /** + * The parameter types. + * + * @var array + */ + protected $types = array(); + + /** + * The underlying driver statement. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $stmt; + + /** + * The underlying database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The connection this statement is bound to and executed on. + * + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param \Doctrine\DBAL\Connection $conn The connection on which the statement should be executed. + */ + public function __construct($sql, Connection $conn) + { + $this->sql = $sql; + $this->stmt = $conn->getWrappedConnection()->prepare($sql); + $this->conn = $conn; + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Binds a parameter value to the statement. + * + * The value can optionally be bound with a PDO binding type or a DBAL mapping type. + * If bound with a DBAL mapping type, the binding type is derived from the mapping + * type and the value undergoes the conversion routines of the mapping type before + * being bound. + * + * @param string $name The name or position of the parameter. + * @param mixed $value The value of the parameter. + * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindValue($name, $value, $type = null) + { + $this->params[$name] = $value; + $this->types[$name] = $type; + if ($type !== null) { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + + return $this->stmt->bindValue($name, $value, $bindingType); + } else { + return $this->stmt->bindValue($name, $value); + } + } + + /** + * Binds a parameter to a value by reference. + * + * Binding a parameter by reference does not support DBAL mapping types. + * + * @param string $name The name or position of the parameter. + * @param mixed $var The reference to the variable to bind. + * @param integer $type The PDO binding type. + * @param integer|null $length Must be specified when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindParam($name, &$var, $type = PDO::PARAM_STR, $length = null) + { + $this->params[$name] = $var; + $this->types[$name] = $type; + + return $this->stmt->bindParam($name, $var, $type, $length); + } + + /** + * Executes the statement with the currently bound parameters. + * + * @param array|null $params + * + * @return boolean TRUE on success, FALSE on failure. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function execute($params = null) + { + if (is_array($params)) { + $this->params = $params; + } + + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + $stmt = $this->stmt->execute($params); + } catch (\Exception $ex) { + if ($logger) { + $logger->stopQuery(); + } + throw DBALException::driverExceptionDuringQuery( + $this->conn->getDriver(), + $ex, + $this->sql, + $this->conn->resolveParams($this->params, $this->types) + ); + } + + if ($logger) { + $logger->stopQuery(); + } + $this->params = array(); + $this->types = array(); + + return $stmt; + } + + /** + * Closes the cursor, freeing the database resources used by this statement. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * Returns the number of columns in the result set. + * + * @return integer + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * Fetches the SQLSTATE associated with the last operation on the statement. + * + * @return string + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * Fetches extended error information associated with the last operation on the statement. + * + * @return array + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 === null) { + return $this->stmt->setFetchMode($fetchMode); + } elseif ($arg3 === null) { + return $this->stmt->setFetchMode($fetchMode, $arg2); + } + + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritdoc} + */ + public function getIterator() + { + return $this->stmt; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + return $this->stmt->fetch($fetchMode); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + if ($fetchArgument) { + return $this->stmt->fetchAll($fetchMode, $fetchArgument); + } + + return $this->stmt->fetchAll($fetchMode); + } + + /** + * Returns a single column from the next row of a result set. + * + * @param integer $columnIndex + * + * @return mixed A single column from the next row of a result set or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->stmt->fetchColumn($columnIndex); + } + + /** + * Returns the number of rows affected by the last execution of this statement. + * + * @return integer The number of affected rows. + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } + + /** + * Gets the wrapped driver statement. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + public function getWrappedStatement() + { + return $this->stmt; + } +} diff --git a/wbce/include/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/wbce/include/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php new file mode 100644 index 000000000..4d76ca92b --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ImportCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:import') + ->setDescription('Import SQL file(s) directly to Database.') + ->setDefinition(array( + new InputArgument( + 'file', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'File path(s) of SQL to be executed.' + ) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($fileNames = $input->getArgument('file')) !== null) { + foreach ((array) $fileNames as $fileName) { + $filePath = realpath($fileName); + + // Phar compatibility. + if (false === $filePath) { + $filePath = $fileName; + } + + if ( ! file_exists($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $filePath) + ); + } elseif ( ! is_readable($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $filePath) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $filePath)); + $sql = file_get_contents($filePath); + + if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // PDO Drivers + try { + $lines = 0; + + $stmt = $conn->prepare($sql); + $stmt->execute(); + + do { + // Required due to "MySQL has gone away!" issue + $stmt->fetch(); + $stmt->closeCursor(); + + $lines++; + } while ($stmt->nextRowset()); + + $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); + } catch (\PDOException $e) { + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } else { + // Non-PDO Drivers (ie. OCI8 driver) + $stmt = $conn->prepare($sql); + $rs = $stmt->execute(); + + if ($rs) { + $output->writeln('OK!' . PHP_EOL); + } else { + $error = $stmt->errorInfo(); + + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($error[2], $error[0]); + } + + $stmt->closeCursor(); + } + } + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/wbce/include/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php new file mode 100644 index 000000000..b8a626fec --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,172 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + /** + * @var array + */ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'mysql57' => 'Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords', + 'sqlserver' => 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords', + 'sqlserver2005' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords', + 'sqlserver2008' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords', + 'sqlserver2012' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'pgsql91' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords', + 'pgsql92' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + 'sqlanywhere' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords', + 'sqlanywhere11' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords', + 'sqlanywhere12' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords', + 'sqlanywhere16' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command. + * + * @param string $name + * @param string $class + * + * @return void + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:reserved-words') + ->setDescription('Checks if the current database contains identifiers that are reserved.') + ->setDefinition([ + new InputOption( + 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Keyword-List name.' + ) + ]) + ->setHelp(<<%command.full_name% + +If you want to check against specific dialects you can +pass them to the command: + + %command.full_name% -l mysql -l pgsql + +The following keyword lists are currently shipped with Doctrine: + + * mysql + * mysql57 + * pgsql + * pgsql92 + * sqlite + * oracle + * sqlserver + * sqlserver2005 + * sqlserver2008 + * sqlserver2012 + * sqlanywhere + * sqlanywhere11 + * sqlanywhere12 + * sqlanywhere16 + * db2 (Not checked by default) +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $conn \Doctrine\DBAL\Connection */ + $conn = $this->getHelper('db')->getConnection(); + + $keywordLists = (array) $input->getOption('list'); + if ( ! $keywordLists) { + $keywordLists = array( + 'mysql', + 'mysql57', + 'pgsql', + 'pgsql92', + 'sqlite', + 'oracle', + 'sqlserver', + 'sqlserver2005', + 'sqlserver2008', + 'sqlserver2012', + 'sqlanywhere', + 'sqlanywhere11', + 'sqlanywhere12', + 'sqlanywhere16', + ); + } + + $keywords = []; + foreach ($keywordLists as $keywordList) { + if (!isset($this->keywordListClasses[$keywordList])) { + throw new \InvalidArgumentException( + "There exists no keyword list with name '" . $keywordList . "'. ". + "Known lists: " . implode(", ", array_keys($this->keywordListClasses)) + ); + } + $class = $this->keywordListClasses[$keywordList]; + $keywords[] = new $class; + } + + $output->write('Checking keyword violations for ' . implode(", ", $keywordLists) . "...", true); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $conn->getSchemaManager()->createSchema(); + $visitor = new ReservedKeywordsValidator($keywords); + $schema->visit($visitor); + + $violations = $visitor->getViolations(); + if (count($violations) == 0) { + $output->write("No reserved keywords violations have been found!", true); + } else { + $output->write('There are ' . count($violations) . ' reserved keyword violations in your database schema:', true); + foreach ($violations as $violation) { + $output->write(' - ' . $violation, true); + } + + return 1; + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/wbce/include/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php new file mode 100644 index 000000000..0b0f7ba2c --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunSqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:run-sql') + ->setDescription('Executes arbitrary SQL directly from the command line.') + ->setDefinition([ + new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), + new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', 7), + new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'), + ]) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($sql = $input->getArgument('sql')) === null) { + throw new \RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + if (stripos($sql, 'select') === 0 || $input->getOption('force-fetch')) { + $resultSet = $conn->fetchAll($sql); + } else { + $resultSet = $conn->executeUpdate($sql); + } + + ob_start(); + \Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth); + $message = ob_get_clean(); + + $output->write($message); + } +} diff --git a/wbce/include/Doctrine/DBAL/Tools/Console/ConsoleRunner.php b/wbce/include/Doctrine/DBAL/Tools/Console/ConsoleRunner.php new file mode 100644 index 000000000..4af8d1284 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Tools/Console/ConsoleRunner.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Tools\Console\Command\ImportCommand; +use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand; +use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Symfony\Component\Console\Application; +use Doctrine\DBAL\Version; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param Connection $connection + * + * @return HelperSet + */ + static public function createHelperSet(Connection $connection) + { + return new HelperSet([ + 'db' => new ConnectionHelper($connection) + ]); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = []) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + self::addCommands($cli); + + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @param Application $cli + * + * @return void + */ + static public function addCommands(Application $cli) + { + $cli->addCommands([ + new RunSqlCommand(), + new ImportCommand(), + new ReservedWordsCommand(), + ]); + } + + /** + * Prints the instructions to create a configuration file + */ + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine-DBAL Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * The Doctrine database Connection. + * + * @var \Doctrine\DBAL\Connection + */ + protected $_connection; + + /** + * Constructor. + * + * @param \Doctrine\DBAL\Connection $connection The Doctrine database Connection. + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves the Doctrine database Connection. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'connection'; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/ArrayType.php b/wbce/include/Doctrine/DBAL/Types/ArrayType.php new file mode 100644 index 000000000..f8c62cb7a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP array to a clob SQL type. + * + * @since 2.0 + */ +class ArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + // @todo 3.0 - $value === null check to save real NULL in database + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value != 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/BigIntType.php b/wbce/include/Doctrine/DBAL/Types/BigIntType.php new file mode 100644 index 000000000..1a9a6c37f --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database BIGINT to a PHP string. + * + * @author robo + * @since 2.0 + */ +class BigIntType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BIGINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (string) $value; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/BinaryType.php b/wbce/include/Doctrine/DBAL/Types/BinaryType.php new file mode 100644 index 000000000..a22a44054 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/BinaryType.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps ab SQL BINARY/VARBINARY to a PHP resource stream. + * + * @author Steve Müller + * @since 2.5 + */ +class BinaryType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBinaryTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BINARY); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BINARY; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/BlobType.php b/wbce/include/Doctrine/DBAL/Types/BlobType.php new file mode 100644 index 000000000..bd89301e2 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL BLOB to a PHP resource stream. + * + * @since 2.2 + */ +class BlobType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BLOB; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/BooleanType.php b/wbce/include/Doctrine/DBAL/Types/BooleanType.php new file mode 100644 index 000000000..9fea80f10 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL boolean to a PHP boolean. + * + * @since 2.0 + */ +class BooleanType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleansToDatabaseValue($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $platform->convertFromBoolean($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BOOLEAN; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_BOOL; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/ConversionException.php b/wbce/include/Doctrine/DBAL/Types/ConversionException.php new file mode 100644 index 000000000..5f91cc308 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,114 @@ +. + */ + +/** + * Conversion Exception is thrown when the database to PHP conversion fails. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +namespace Doctrine\DBAL\Types; + +class ConversionException extends \Doctrine\DBAL\DBALException +{ + /** + * Thrown when a Database to Doctrine Type Conversion fails. + * + * @param string $value + * @param string $toType + * + * @return \Doctrine\DBAL\Types\ConversionException + */ + static public function conversionFailed($value, $toType) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . '...' : $value; + + return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType); + } + + /** + * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement + * about the expected format. + * + * @param string $value + * @param string $toType + * @param string $expectedFormat + * @param \Exception|null $previous + * + * @return \Doctrine\DBAL\Types\ConversionException + */ + static public function conversionFailedFormat($value, $toType, $expectedFormat, \Exception $previous = null) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . '...' : $value; + + return new self( + 'Could not convert database value "' . $value . '" to Doctrine Type ' . + $toType . '. Expected format: ' . $expectedFormat, + 0, + $previous + ); + } + + /** + * Thrown when the PHP value passed to the converter was not of the expected type. + * + * @param mixed $value + * @param string $toType + * @param string[] $possibleTypes + * + * @return \Doctrine\DBAL\Types\ConversionException + */ + static public function conversionFailedInvalidType($value, $toType, array $possibleTypes) + { + $actualType = is_object($value) ? get_class($value) : gettype($value); + + if (is_scalar($value)) { + return new self(sprintf( + "Could not convert PHP value '%s' of type '%s' to type '%s'. Expected one of the following types: %s", + $value, + $actualType, + $toType, + implode(', ', $possibleTypes) + )); + } + + return new self(sprintf( + "Could not convert PHP value of type '%s' to type '%s'. Expected one of the following types: %s", + $actualType, + $toType, + implode(', ', $possibleTypes) + )); + } + + static public function conversionFailedSerialization($value, $format, $error) + { + $actualType = is_object($value) ? get_class($value) : gettype($value); + + return new self(sprintf( + "Could not convert PHP type '%s' to '%s', as an '%s' error was triggered by the serialization", + $actualType, + $format, + $error + )); + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateImmutableType.php b/wbce/include/Doctrine/DBAL/Types/DateImmutableType.php new file mode 100644 index 000000000..8b9a91d7d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateImmutableType.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Immutable type of {@see DateType}. + * + * @since 2.6 + * @author Steve Müller + */ +class DateImmutableType extends DateType +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATE_IMMUTABLE; + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeImmutable) { + return $value->format($platform->getDateFormatString()); + } + + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', \DateTimeImmutable::class] + ); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeImmutable) { + return $value; + } + + $dateTime = \DateTimeImmutable::createFromFormat('!' . $platform->getDateFormatString(), $value); + + if (! $dateTime) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateFormatString() + ); + } + + return $dateTime; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateIntervalType.php b/wbce/include/Doctrine/DBAL/Types/DateIntervalType.php new file mode 100644 index 000000000..4ad9c42f2 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateIntervalType.php @@ -0,0 +1,70 @@ +getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if ($value instanceof \DateInterval) { + return $value->format('P%YY%MM%DDT%HH%IM%SS'); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateInterval']); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateInterval) { + return $value; + } + + try { + return new \DateInterval($value); + } catch (\Exception $exception) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), 'P%YY%MM%DDT%HH%IM%SS', $exception); + } + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateTimeImmutableType.php b/wbce/include/Doctrine/DBAL/Types/DateTimeImmutableType.php new file mode 100644 index 000000000..b8564fcdd --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateTimeImmutableType.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Immutable type of {@see DateTimeType}. + * + * @since 2.6 + * @author Steve Müller + */ +class DateTimeImmutableType extends DateTimeType +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIME_IMMUTABLE; + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeImmutable) { + return $value->format($platform->getDateTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', \DateTimeImmutable::class] + ); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeImmutable) { + return $value; + } + + $dateTime = \DateTimeImmutable::createFromFormat($platform->getDateTimeFormatString(), $value); + + if (! $dateTime) { + $dateTime = \date_create_immutable($value); + } + + if (! $dateTime) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateTimeFormatString() + ); + } + + return $dateTime; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateTimeType.php b/wbce/include/Doctrine/DBAL/Types/DateTimeType.php new file mode 100644 index 000000000..a1830dce4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object. + * + * @since 2.0 + */ +class DateTimeType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeInterface) { + return $value->format($platform->getDateTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeInterface) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + + if ( ! $val) { + $val = date_create($value); + } + + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateTimeTzImmutableType.php b/wbce/include/Doctrine/DBAL/Types/DateTimeTzImmutableType.php new file mode 100644 index 000000000..91f3eda75 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateTimeTzImmutableType.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Immutable type of {@see DateTimeTzType}. + * + * @since 2.6 + * @author Steve Müller + */ +class DateTimeTzImmutableType extends DateTimeTzType +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIMETZ_IMMUTABLE; + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeImmutable) { + return $value->format($platform->getDateTimeTzFormatString()); + } + + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', \DateTimeImmutable::class] + ); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeImmutable) { + return $value; + } + + $dateTime = \DateTimeImmutable::createFromFormat($platform->getDateTimeTzFormatString(), $value); + + if (! $dateTime) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getDateTimeTzFormatString() + ); + } + + return $dateTime; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateTimeTzType.php b/wbce/include/Doctrine/DBAL/Types/DateTimeTzType.php new file mode 100644 index 000000000..e73bcc907 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,97 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * DateTime type saving additional timezone information. + * + * Caution: Databases are not necessarily experts at storing timezone related + * data of dates. First, of all the supported vendors only PostgreSQL and Oracle + * support storing Timezone data. But those two don't save the actual timezone + * attached to a DateTime instance (for example "Europe/Berlin" or "America/Montreal") + * but the current offset of them related to UTC. That means depending on daylight saving times + * or not you may get different offsets. + * + * This datatype makes only sense to use, if your application works with an offset, not + * with an actual timezone that uses transitions. Otherwise your DateTime instance + * attached with a timezone such as Europe/Berlin gets saved into the database with + * the offset and re-created from persistence with only the offset, not the original timezone + * attached. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIMETZ; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeInterface) { + return $value->format($platform->getDateTimeTzFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeInterface) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeTzFormatString()); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DateType.php b/wbce/include/Doctrine/DBAL/Types/DateType.php new file mode 100644 index 000000000..e3ed6a9e7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATE to a PHP Date object. + * + * @since 2.0 + */ +class DateType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATE; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeInterface) { + return $value->format($platform->getDateFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeInterface) { + return $value; + } + + $val = \DateTime::createFromFormat('!'.$platform->getDateFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateFormatString()); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/DecimalType.php b/wbce/include/Doctrine/DBAL/Types/DecimalType.php new file mode 100644 index 000000000..42c05d69a --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP string. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DECIMAL; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/FloatType.php b/wbce/include/Doctrine/DBAL/Types/FloatType.php new file mode 100644 index 000000000..87f9c32ae --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::FLOAT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (double) $value; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/GuidType.php b/wbce/include/Doctrine/DBAL/Types/GuidType.php new file mode 100644 index 000000000..761d58a8e --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/GuidType.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Represents a GUID/UUID datatype (both are actually synonyms) in the database. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class GuidType extends StringType +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::GUID; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return !$platform->hasNativeGuidType(); + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/IntegerType.php b/wbce/include/Doctrine/DBAL/Types/IntegerType.php new file mode 100644 index 000000000..6fe6375ec --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL INT to a PHP integer. + * + * @author Roman Borschel + * @since 2.0 + */ +class IntegerType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::INTEGER; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/JsonArrayType.php b/wbce/include/Doctrine/DBAL/Types/JsonArrayType.php new file mode 100644 index 000000000..af5b54134 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/JsonArrayType.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used to generate json arrays. + * + * @since 2.3 + * @deprecated Use JsonType instead + * @author Johannes M. Schmitt + */ +class JsonArrayType extends JsonType +{ + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value === '') { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return json_decode($value, true); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::JSON_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/JsonType.php b/wbce/include/Doctrine/DBAL/Types/JsonType.php new file mode 100644 index 000000000..7db496c14 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/JsonType.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type generating json objects values + * + * @since 2.6 + * @author Baptiste Clavié + */ +class JsonType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + $encoded = json_encode($value); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw ConversionException::conversionFailedSerialization($value, 'json', $this->getLastErrorMessage()); + } + + return $encoded; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value === '') { + return null; + } + + if (is_resource($value)) { + $value = stream_get_contents($value); + } + + $val = json_decode($value, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::JSON; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return ! $platform->hasNativeJsonType(); + } + + /** + * Get the latest json error message + * + * This method declaration has been extracted from symfony's php 5.5 polyfill + * + * @link https://github.com/symfony/polyfill-php55/blob/master/Php55.php + * @link http://nl1.php.net/manual/en/function.json-last-error-msg.php + * + * @return string + */ + private function getLastErrorMessage() + { + if (function_exists('json_last_error_msg')) { + return json_last_error_msg(); + } + + switch (json_last_error()) { + case JSON_ERROR_NONE: + return 'No error'; + + case JSON_ERROR_DEPTH: + return 'Maximum stack depth exceeded'; + + case JSON_ERROR_STATE_MISMATCH: + return 'State mismatch (invalid or malformed JSON)'; + + case JSON_ERROR_CTRL_CHAR: + return 'Control character error, possibly incorrectly encoded'; + + case JSON_ERROR_SYNTAX: + return 'Syntax error'; + + case JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + + default: + return 'Unknown error'; + } + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/ObjectType.php b/wbce/include/Doctrine/DBAL/Types/ObjectType.php new file mode 100644 index 000000000..0b1f87a23 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP object to a clob SQL type. + * + * @since 2.0 + */ +class ObjectType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value !== 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::OBJECT; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/SimpleArrayType.php b/wbce/include/Doctrine/DBAL/Types/SimpleArrayType.php new file mode 100644 index 000000000..47c821be4 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/SimpleArrayType.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used for simple values. + * + * Only use this type if you are sure that your values cannot contain a ",". + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class SimpleArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!$value) { + return null; + } + + return implode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return explode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SIMPLE_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/SmallIntType.php b/wbce/include/Doctrine/DBAL/Types/SmallIntType.php new file mode 100644 index 000000000..7ebae6ebe --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database SMALLINT to a PHP integer. + * + * @author robo + */ +class SmallIntType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SMALLINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/StringType.php b/wbce/include/Doctrine/DBAL/Types/StringType.php new file mode 100644 index 000000000..39bd32dbc --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL VARCHAR to a PHP string. + * + * @since 2.0 + */ +class StringType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::STRING; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/TextType.php b/wbce/include/Doctrine/DBAL/Types/TextType.php new file mode 100644 index 000000000..4055ac88d --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL CLOB to a PHP string. + * + * @since 2.0 + */ +class TextType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TEXT; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/TimeImmutableType.php b/wbce/include/Doctrine/DBAL/Types/TimeImmutableType.php new file mode 100644 index 000000000..118d80ea8 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/TimeImmutableType.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Immutable type of {@see TimeType}. + * + * @since 2.6 + * @author Steve Müller + */ +class TimeImmutableType extends TimeType +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TIME_IMMUTABLE; + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeImmutable) { + return $value->format($platform->getTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', \DateTimeImmutable::class] + ); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeImmutable) { + return $value; + } + + $dateTime = \DateTimeImmutable::createFromFormat('!' . $platform->getTimeFormatString(), $value); + + if (! $dateTime) { + throw ConversionException::conversionFailedFormat( + $value, + $this->getName(), + $platform->getTimeFormatString() + ); + } + + return $dateTime; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/TimeType.php b/wbce/include/Doctrine/DBAL/Types/TimeType.php new file mode 100644 index 000000000..639533fad --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL TIME to a PHP DateTime object. + * + * @since 2.0 + */ +class TimeType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeInterface) { + return $value->format($platform->getTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'DateTime']); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeInterface) { + return $value; + } + + $val = \DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getTimeFormatString()); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/Type.php b/wbce/include/Doctrine/DBAL/Types/Type.php new file mode 100644 index 000000000..1379bb203 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,353 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\DBALException; + +/** + * The base class for so-called Doctrine mapping types. + * + * A Type object is obtained by calling the static {@link getType()} method. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class Type +{ + const TARRAY = 'array'; + const SIMPLE_ARRAY = 'simple_array'; + const JSON_ARRAY = 'json_array'; + const JSON = 'json'; + const BIGINT = 'bigint'; + const BOOLEAN = 'boolean'; + const DATETIME = 'datetime'; + const DATETIME_IMMUTABLE = 'datetime_immutable'; + const DATETIMETZ = 'datetimetz'; + const DATETIMETZ_IMMUTABLE = 'datetimetz_immutable'; + const DATE = 'date'; + const DATE_IMMUTABLE = 'date_immutable'; + const TIME = 'time'; + const TIME_IMMUTABLE = 'time_immutable'; + const DECIMAL = 'decimal'; + const INTEGER = 'integer'; + const OBJECT = 'object'; + const SMALLINT = 'smallint'; + const STRING = 'string'; + const TEXT = 'text'; + const BINARY = 'binary'; + const BLOB = 'blob'; + const FLOAT = 'float'; + const GUID = 'guid'; + const DATEINTERVAL = 'dateinterval'; + + /** + * Map of already instantiated type objects. One instance per type (flyweight). + * + * @var array + */ + private static $_typeObjects = array(); + + /** + * The map of supported doctrine mapping types. + * + * @var array + */ + private static $_typesMap = array( + self::TARRAY => ArrayType::class, + self::SIMPLE_ARRAY => SimpleArrayType::class, + self::JSON_ARRAY => JsonArrayType::class, + self::JSON => JsonType::class, + self::OBJECT => ObjectType::class, + self::BOOLEAN => BooleanType::class, + self::INTEGER => IntegerType::class, + self::SMALLINT => SmallIntType::class, + self::BIGINT => BigIntType::class, + self::STRING => StringType::class, + self::TEXT => TextType::class, + self::DATETIME => DateTimeType::class, + self::DATETIME_IMMUTABLE => DateTimeImmutableType::class, + self::DATETIMETZ => DateTimeTzType::class, + self::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class, + self::DATE => DateType::class, + self::DATE_IMMUTABLE => DateImmutableType::class, + self::TIME => TimeType::class, + self::TIME_IMMUTABLE => TimeImmutableType::class, + self::DECIMAL => DecimalType::class, + self::FLOAT => FloatType::class, + self::BINARY => BinaryType::class, + self::BLOB => BlobType::class, + self::GUID => GuidType::class, + self::DATEINTERVAL => DateIntervalType::class, + ); + + /** + * Prevents instantiation and forces use of the factory method. + */ + final private function __construct() + { + } + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Gets the default length of this type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return integer|null + * + * @todo Needed? + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return null; + } + + /** + * Gets the SQL declaration snippet for a field of this type. + * + * @param array $fieldDeclaration The field declaration. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return string + */ + abstract public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); + + /** + * Gets the name of this type. + * + * @return string + * + * @todo Needed? + */ + abstract public function getName(); + + /** + * Factory method to create type instances. + * Type instances are implemented as flyweights. + * + * @param string $name The name of the type (as returned by getName()). + * + * @return \Doctrine\DBAL\Types\Type + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function getType($name) + { + if ( ! isset(self::$_typeObjects[$name])) { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::unknownColumnType($name); + } + self::$_typeObjects[$name] = new self::$_typesMap[$name](); + } + + return self::$_typeObjects[$name]; + } + + /** + * Adds a custom type to the type map. + * + * @param string $name The name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function addType($name, $className) + { + if (isset(self::$_typesMap[$name])) { + throw DBALException::typeExists($name); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Checks if exists support for a type. + * + * @param string $name The name of the type. + * + * @return boolean TRUE if type is supported; FALSE otherwise. + */ + public static function hasType($name) + { + return isset(self::$_typesMap[$name]); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function overrideType($name, $className) + { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::typeNotFound($name); + } + + if (isset(self::$_typeObjects[$name])) { + unset(self::$_typeObjects[$name]); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Gets the (preferred) binding type for values of this type that + * can be used when binding parameters to prepared statements. + * + * This method should return one of the PDO::PARAM_* constants, that is, one of: + * + * PDO::PARAM_BOOL + * PDO::PARAM_NULL + * PDO::PARAM_INT + * PDO::PARAM_STR + * PDO::PARAM_LOB + * + * @return integer + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * Gets the types array map which holds all registered types and the corresponding + * type class + * + * @return array + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + /** + * @return string + */ + public function __toString() + { + $e = explode('\\', get_class($this)); + + return str_replace('Type', '', end($e)); + } + + /** + * Does working with this column require SQL conversion functions? + * + * This is a metadata function that is required for example in the ORM. + * Usage of {@link convertToDatabaseValueSQL} and + * {@link convertToPHPValueSQL} works for any type and mostly + * does nothing. This method can additionally be used for optimization purposes. + * + * @return boolean + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return $sqlExpr; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. + * + * @param string $sqlExpr + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Gets an array of database types that map to this Doctrine type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMappedDatabaseTypes(AbstractPlatform $platform) + { + return array(); + } + + /** + * If this Doctrine Type maps to an already mapped database type, + * reverse schema engineering can't take them apart. You need to mark + * one of those types as commented, which will have Doctrine use an SQL + * comment to typehint the actual Doctrine Type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return boolean + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return false; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/VarDateTimeImmutableType.php b/wbce/include/Doctrine/DBAL/Types/VarDateTimeImmutableType.php new file mode 100644 index 000000000..3a3735b04 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/VarDateTimeImmutableType.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Immutable type of {@see VarDateTimeType}. + * + * @since 2.6 + * @author Steve Müller + */ +class VarDateTimeImmutableType extends VarDateTimeType +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIME_IMMUTABLE; + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return $value; + } + + if ($value instanceof \DateTimeImmutable) { + return $value->format($platform->getDateTimeFormatString()); + } + + throw ConversionException::conversionFailedInvalidType( + $value, + $this->getName(), + ['null', \DateTimeImmutable::class] + ); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTimeImmutable) { + return $value; + } + + $dateTime = date_create_immutable($value); + + if (! $dateTime) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $dateTime; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/wbce/include/Doctrine/DBAL/Types/VarDateTimeType.php b/wbce/include/Doctrine/DBAL/Types/VarDateTimeType.php new file mode 100644 index 000000000..5cf35f795 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Variable DateTime Type using date_create() instead of DateTime::createFromFormat(). + * + * This type has performance implications as it runs twice as long as the regular + * {@see DateTimeType}, however in certain PostgreSQL configurations with + * TIMESTAMP(n) columns where n > 0 it is necessary to use this type. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = date_create($value); + if ( ! $val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } +} diff --git a/wbce/include/Doctrine/DBAL/Version.php b/wbce/include/Doctrine/DBAL/Version.php new file mode 100644 index 000000000..6e1da9ba1 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.6.3'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version The Doctrine version to compare to. + * + * @return integer -1 if older, 0 if it is the same, 1 if version passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/wbce/include/Doctrine/DBAL/VersionAwarePlatformDriver.php b/wbce/include/Doctrine/DBAL/VersionAwarePlatformDriver.php new file mode 100644 index 000000000..53e2d3ab7 --- /dev/null +++ b/wbce/include/Doctrine/DBAL/VersionAwarePlatformDriver.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contract for a driver that is able to create platform instances by version. + * + * Doctrine uses different platform classes for different vendor versions to + * support the correct features and SQL syntax of each version. + * This interface should be implemented by drivers that are capable to do this + * distinction. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface VersionAwarePlatformDriver +{ + /** + * Factory method for creating the appropriate platform instance for the given version. + * + * @param string $version The platform/server version string to evaluate. This should be given in the notation + * the underlying database vendor uses. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + * + * @throws DBALException if the given version string could not be evaluated. + */ + public function createDatabasePlatformForVersion($version); +} diff --git a/wbce/include/Doctrine/index.php b/wbce/include/Doctrine/index.php new file mode 100644 index 000000000..f57b490f3 --- /dev/null +++ b/wbce/include/Doctrine/index.php @@ -0,0 +1,2 @@ +