1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Data\Persistence\Sql\Mssql;
6:
7: use Doctrine\DBAL\Platforms\AbstractPlatform;
8: use Doctrine\DBAL\Schema\Index;
9:
10: trait PlatformTrait
11: {
12: #[\Override]
13: public function getVarcharTypeDeclarationSQL(array $column)
14: {
15: $column['length'] = ($column['length'] ?? 255) * 4;
16:
17: return parent::getVarcharTypeDeclarationSQL($column);
18: }
19:
20: // remove once https://github.com/doctrine/dbal/pull/4987 is fixed
21: // and also $this->markDoctrineTypeCommented('text') below
22: #[\Override]
23: public function getClobTypeDeclarationSQL(array $column)
24: {
25: $res = parent::getClobTypeDeclarationSQL($column);
26:
27: return (str_starts_with($res, 'VARCHAR') ? 'N' : '') . $res;
28: }
29:
30: // TODO test DBAL DB diff for each supported Field type
31: // then fix using https://github.com/doctrine/dbal/issues/5194#issuecomment-1018790220
32: /* protected function initializeCommentedDoctrineTypes()
33: {
34: parent::initializeCommentedDoctrineTypes();
35:
36: $this->markDoctrineTypeCommented('text');
37: } */
38:
39: #[\Override]
40: public function getCurrentDatabaseExpression(bool $includeSchema = false): string
41: {
42: if ($includeSchema) {
43: return 'CONCAT(DB_NAME(), \'.\', SCHEMA_NAME())';
44: }
45:
46: return parent::getCurrentDatabaseExpression();
47: }
48:
49: #[\Override]
50: public function getCreateIndexSQL(Index $index, $table)
51: {
52: // workaround https://github.com/doctrine/dbal/issues/5507
53: // no side effect on DBAL index list observed, but multiple null values cannot be inserted
54: // the only, very complex, solution would be using intermediate view
55: // SQL Server should be fixed to allow FK creation when there is an unique index
56: // with "WHERE xxx IS NOT NULL" as FK does not restrict NULL values anyway
57: return $index->hasFlag('atk4-not-null')
58: ? AbstractPlatform::getCreateIndexSQL($index, $table)
59: : parent::getCreateIndexSQL($index, $table);
60: }
61:
62: // SQL Server DBAL platform has buggy identifier escaping, fix until fixed officially, see:
63: // https://github.com/doctrine/dbal/pull/4360
64:
65: #[\Override]
66: protected function getCreateColumnCommentSQL($tableName, $columnName, $comment)
67: {
68: if (str_contains($tableName, '.')) {
69: [$schemaName, $tableName] = explode('.', $tableName, 2);
70: } else {
71: $schemaName = 'dbo';
72: }
73:
74: return $this->getAddExtendedPropertySQL(
75: 'MS_Description',
76: (string) $comment,
77: 'SCHEMA',
78: $schemaName,
79: 'TABLE',
80: $tableName,
81: 'COLUMN',
82: $columnName
83: );
84: }
85:
86: #[\Override]
87: protected function getAlterColumnCommentSQL($tableName, $columnName, $comment)
88: {
89: if (str_contains($tableName, '.')) {
90: [$schemaName, $tableName] = explode('.', $tableName, 2);
91: } else {
92: $schemaName = 'dbo';
93: }
94:
95: return $this->getUpdateExtendedPropertySQL(
96: 'MS_Description',
97: (string) $comment,
98: 'SCHEMA',
99: $schemaName,
100: 'TABLE',
101: $tableName,
102: 'COLUMN',
103: $columnName
104: );
105: }
106:
107: #[\Override]
108: protected function getDropColumnCommentSQL($tableName, $columnName)
109: {
110: if (str_contains($tableName, '.')) {
111: [$schemaName, $tableName] = explode('.', $tableName, 2);
112: } else {
113: $schemaName = 'dbo';
114: }
115:
116: return $this->getDropExtendedPropertySQL(
117: 'MS_Description',
118: 'SCHEMA',
119: $schemaName,
120: 'TABLE',
121: $tableName,
122: 'COLUMN',
123: $columnName
124: );
125: }
126:
127: private function quoteSingleIdentifierAsStringLiteral(string $levelName): string
128: {
129: return $this->quoteStringLiteral(preg_replace('~^\[|\]$~', '', $levelName));
130: }
131:
132: #[\Override]
133: public function getAddExtendedPropertySQL(
134: $name,
135: $value = null,
136: $level0Type = null,
137: $level0Name = null,
138: $level1Type = null,
139: $level1Name = null,
140: $level2Type = null,
141: $level2Name = null
142: ) {
143: return 'EXEC sp_addextendedproperty'
144: . ' N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value)
145: . ', N' . $this->quoteStringLiteral((string) $level0Type)
146: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level0Name)
147: . ', N' . $this->quoteStringLiteral((string) $level1Type)
148: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level1Name)
149: . (
150: $level2Type !== null || $level2Name !== null
151: ? ', N' . $this->quoteStringLiteral((string) $level2Type)
152: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level2Name)
153: : ''
154: );
155: }
156:
157: #[\Override]
158: public function getDropExtendedPropertySQL(
159: $name,
160: $level0Type = null,
161: $level0Name = null,
162: $level1Type = null,
163: $level1Name = null,
164: $level2Type = null,
165: $level2Name = null
166: ) {
167: return 'EXEC sp_dropextendedproperty'
168: . ' N' . $this->quoteStringLiteral($name)
169: . ', N' . $this->quoteStringLiteral((string) $level0Type)
170: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level0Name)
171: . ', N' . $this->quoteStringLiteral((string) $level1Type)
172: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level1Name)
173: . (
174: $level2Type !== null || $level2Name !== null
175: ? ', N' . $this->quoteStringLiteral((string) $level2Type)
176: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level2Name)
177: : ''
178: );
179: }
180:
181: #[\Override]
182: public function getUpdateExtendedPropertySQL(
183: $name,
184: $value = null,
185: $level0Type = null,
186: $level0Name = null,
187: $level1Type = null,
188: $level1Name = null,
189: $level2Type = null,
190: $level2Name = null
191: ) {
192: return 'EXEC sp_updateextendedproperty'
193: . ' N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value)
194: . ', N' . $this->quoteStringLiteral((string) $level0Type)
195: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level0Name)
196: . ', N' . $this->quoteStringLiteral((string) $level1Type)
197: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level1Name)
198: . (
199: $level2Type !== null || $level2Name !== null
200: ? ', N' . $this->quoteStringLiteral((string) $level2Type)
201: . ', ' . $this->quoteSingleIdentifierAsStringLiteral((string) $level2Name)
202: : ''
203: );
204: }
205:
206: #[\Override]
207: protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
208: {
209: if (str_contains($tableName, '.')) {
210: [$schemaName, $tableName] = explode('.', $tableName, 2);
211: } else {
212: $schemaName = 'dbo';
213: }
214:
215: return $this->getAddExtendedPropertySQL(
216: 'MS_Description',
217: (string) $comment,
218: 'SCHEMA',
219: $schemaName,
220: 'TABLE',
221: $tableName
222: );
223: }
224: }
225: