Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
blockinserter.php
1<?
10
11use Bitrix\Main;
13
15{
16 protected $tableName = '';
17 protected $tableMap = array();
18 protected $fldVector = array();
19
20 protected $insertHead = '';
21 protected $insertTail = '';
22 protected $index = 0;
23 protected $bufferSize = 0;
24 protected $buffer = '';
25 protected $map = array();
26
27 protected $autoIncFld = false;
28 protected $dbType = false;
29 protected $mtu = 0;
30
31 protected $dbConnection = null;
32 protected $dbHelper = null;
33
34 protected $callbacks = array();
35
36 const RED_LINE = 100;
37
38 public function __construct($parameters = array())
39 {
40 $this->dbConnection = Main\HttpApplication::getConnection();
41 $this->dbHelper = $this->dbConnection->getSqlHelper();
42
43 $map = array();
44 $entityName = trim((string)($parameters['entityName'] ?? ''));
45 $tableName = trim((string)($parameters['tableName'] ?? ''));
46 if ($entityName !== '' && is_a($entityName, Main\ORM\Data\DataManager::class, true))
47 {
48 $table = $entityName;
49
51 $this->tableName = $table::getTableName();
52 $this->tableMap = $table::getMap();
53
54 // filter map throught $parameters['exactFields']
55 if (!empty($parameters['exactFields']) && is_array($parameters['exactFields']))
56 {
57 foreach($parameters['exactFields'] as $fld)
58 {
59 if(!isset($this->tableMap[$fld]))
60 {
61 throw new Main\SystemException('Field does not exist in ORM class, but present in "exactFields" parameter: '.$fld, 0, __FILE__, __LINE__);
62 }
63
64 $map[] = $fld;
65 $this->fldVector[$fld] = true;
66 }
67 }
68 else
69 {
70 foreach($this->tableMap as $fld => $params)
71 {
72 $map[] = $fld;
73 $this->fldVector[$fld] = true;
74 }
75 }
76 }
77 elseif ($tableName !== '')
78 {
79 $this->tableName = $this->dbHelper->forSql($parameters['tableName']);
80 $this->tableMap = [];
81 if (!empty($parameters['exactFields']) && is_array($parameters['exactFields']))
82 {
83 $this->tableMap = $parameters['exactFields'];
84 }
85
86 // $this->tableMap as $fld => $params - is the right way!
87 /*
88 required for
89
90 $loc2site = new DB\BlockInserter(array(
91 'tableName' => 'b_sale_loc_2site',
92 'exactFields' => array(
93 'LOCATION_ID' => array('data_type' => 'integer'),
94 'SITE_ID' => array('data_type' => 'string')
95 ),
96 'parameters' => array(
97 'mtu' => 9999,
98 'autoIncrementFld' => 'ID'
99 )
100 ));
101 */
102 foreach($this->tableMap as $fld => $params)
103 {
104 $map[] = $fld;
105 $this->fldVector[$fld] = true;
106 }
107 }
108
109 // automatically insert to this field an auto-increment value
110 // beware of TransactSQL`s IDENTITY_INSERT when setting autoIncrementFld to a database-driven auto-increment field
111 $autoIncrementFld = trim((string)($parameters['parameters']['autoIncrementFld'] ?? ''));
112 if ($autoIncrementFld !== '')
113 {
114 $this->autoIncFld = $autoIncrementFld;
115 if (!isset($this->fldVector[$this->autoIncFld]))
116 {
118 $this->fldVector[$this->autoIncFld] = true;
119 $this->tableMap[$this->autoIncFld] = [
120 'data_type' => 'integer',
121 ];
122 }
123
124 $this->initIndexFromField();
125 }
126
127 $this->dbType = Main\HttpApplication::getConnection()->getType();
128
129 $this->mtu = (int)($parameters['parameters']['mtu'] ?? 0);
130 if ($this->mtu <= 0)
131 {
132 $this->mtu = 9999;
133 }
134
135 $this->mtu = min($this->mtu, (int)Helper::getMaxTransferUnit());
136
137 $this->insertHead = Helper::getBatchInsertHead($this->tableName, $map);
138 $this->insertTail = Helper::getBatchInsertTail();
139
140 if (
141 isset($parameters['parameters']['CALLBACKS']['ON_BEFORE_FLUSH'])
142 && is_callable($parameters['parameters']['CALLBACKS']['ON_BEFORE_FLUSH'])
143 )
144 {
145 $this->callbacks['ON_BEFORE_FLUSH'] = $parameters['parameters']['CALLBACKS']['ON_BEFORE_FLUSH'];
146 }
147
148 $this->map = $map;
149 }
150
151 // this method is buggy when table is empty
152 public function initIndexFromField($fld = 'ID')
153 {
154 if($fld == '')
155 throw new Main\SystemException('Field is not set');
156
157 $fld = $this->dbHelper->forSql($fld);
158
159 $sql = 'select MAX('.$fld.') as VAL from '.$this->tableName;
160
161 $res = $this->dbConnection->query($sql)->fetch();
162 $this->index = intval($res['VAL']);
163
164 /*
165 $sql = 'select '.$fld.' from '.$this->tableName.' order by '.$fld.' desc';
166 $sql = $this->dbHelper->getTopSql($sql, 1);
167
168 $res = $this->dbConnection->query($sql)->fetch();
169 $this->index = intval($res[$this->autoIncFld]);
170 */
171
172 return $this->index;
173 }
174
175 public function getIndex()
176 {
177 return $this->index;
178 }
179
181 {
182 if($this->autoIncFld === false)
183 return false;
184
185 return Helper::dropAutoIncrementRestrictions($this->tableName);
186 }
187
189 {
190 if($this->autoIncFld === false)
191 return false;
192
193 return Helper::restoreAutoIncrementRestrictions($this->tableName);
194 }
195
197 {
198 Helper::resetAutoIncrement($this->tableName, $this->getIndex() + 1);
199 }
200
201 public function insert($row)
202 {
203 if(!is_array($row) || empty($row))
204 return;
205
206 $this->index++;
207 $this->bufferSize++;
208
209 if($this->autoIncFld !== false)
210 {
212 Helper::incrementSequenceForTable($this->tableName); // if this is oracle and we insert auto increment key directly, we must provide sequence increment manually
213 }
214
215 $sql = Helper::getBatchInsertValues($row, $this->tableName, $this->fldVector, $this->map);
216
217 /*
218 MySQL & MsSQL: insert into b_test (F1,F2) values ('one','two'),('one1','two1'),('one2','two2')
219 Oracle: insert all into b_test (F1,F2) values ('one','two') into b_test (F1,F2) values ('one1','two1') into b_test (F1,F2) values ('one2','two2') select * from dual
220 */
221
222 $nextBuffer = (empty($this->buffer) ? $this->insertHead : $this->buffer.Helper::getBatchInsertSeparator()).$sql;
223
224 // here check length
225 if(defined(SITE_CHARSET) && SITE_CHARSET == 'UTF-8')
226 $len = mb_strlen($nextBuffer);
227 else
228 $len = mb_strlen($nextBuffer);
229
230 if(($this->mtu - (mb_strlen($nextBuffer) + 100)) < self::RED_LINE)
231 {
232 $this->flush(); // flushing the previous buffer (now $this->buffer == '')
233 $this->buffer = $this->insertHead.$sql;
234 }
235 else
236 $this->buffer = $nextBuffer;
237
238 return $this->index;
239 }
240
241 public function flush()
242 {
243 if($this->buffer == '')
244 return;
245
246 if(isset($this->callbacks['ON_BEFORE_FLUSH']))
247 call_user_func($this->callbacks['ON_BEFORE_FLUSH']);
248
249 $this->buffer .= ' '.$this->insertTail;
250
251 $restrDropped = $this->dropAutoIncrementRestrictions();
252
253 Main\HttpApplication::getConnection()->query($this->buffer);
254
255 if($restrDropped)
257
258 $this->buffer = '';
259 $this->bufferSize = 0;
260 }
261}
static getBatchInsertHead($tableName, $fields=array())
static incrementSequenceForTable($tableName)
static getBatchInsertValues($row, $tableName, $fields, $map)
static restoreAutoIncrementRestrictions($tableName)
static dropAutoIncrementRestrictions($tableName)
static resetAutoIncrement($tableName, $startIndex=1)
Definition helper.php:118