1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* |
4
|
|
|
* This file is part of Aura for PHP. |
5
|
|
|
* |
6
|
|
|
* @license http://opensource.org/licenses/bsd-license.php BSD |
7
|
|
|
* |
8
|
|
|
*/ |
9
|
|
|
namespace Aura\SqlQuery\Common; |
10
|
|
|
|
11
|
|
|
use Aura\SqlQuery\AbstractDmlQuery; |
12
|
|
|
use Aura\SqlQuery\Exception; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* |
16
|
|
|
* An object for INSERT queries. |
17
|
|
|
* |
18
|
|
|
* @package Aura.SqlQuery |
19
|
|
|
* |
20
|
|
|
*/ |
21
|
|
|
class Insert extends AbstractDmlQuery implements InsertInterface |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* |
25
|
|
|
* The table to insert into. |
26
|
|
|
* |
27
|
|
|
* @var string |
28
|
|
|
* |
29
|
|
|
*/ |
30
|
|
|
protected $into; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* |
34
|
|
|
* A map of fully-qualified `table.column` names to last-insert-id names. |
35
|
|
|
* This is used to look up the right last-insert-id name for a given table |
36
|
|
|
* and column. Generally useful only for extended tables in Posgres. |
37
|
|
|
* |
38
|
|
|
* @var array |
39
|
|
|
* |
40
|
|
|
*/ |
41
|
|
|
protected $last_insert_id_names; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* |
45
|
|
|
* The current row-number we are adding column values for. This comes into |
46
|
|
|
* play only with bulk inserts. |
47
|
|
|
* |
48
|
|
|
* @var int |
49
|
|
|
* |
50
|
|
|
*/ |
51
|
|
|
protected $row = 0; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* |
55
|
|
|
* A collection of `$col_values` for previous rows in bulk inserts. |
56
|
|
|
* |
57
|
|
|
* @var array |
58
|
|
|
* |
59
|
|
|
*/ |
60
|
|
|
protected $col_values_bulk = array(); |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* |
64
|
|
|
* A collection of `$bind_values` for previous rows in bulk inserts. |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
* |
68
|
|
|
*/ |
69
|
|
|
protected $bind_values_bulk = array(); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* |
73
|
|
|
* The order in which columns will be bulk-inserted; this is taken from the |
74
|
|
|
* very first inserted row. |
75
|
|
|
* |
76
|
|
|
* @var array |
77
|
|
|
* |
78
|
|
|
*/ |
79
|
|
|
protected $col_order = array(); |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* |
83
|
|
|
* Sets the map of fully-qualified `table.column` names to last-insert-id |
84
|
|
|
* names. Generally useful only for extended tables in Posgres. |
85
|
|
|
* |
86
|
|
|
* @param array $last_insert_id_names The list of ID names. |
87
|
|
|
* |
88
|
|
|
*/ |
89
|
76 |
|
public function setLastInsertIdNames(array $last_insert_id_names) |
90
|
|
|
{ |
91
|
76 |
|
$this->last_insert_id_names = $last_insert_id_names; |
92
|
76 |
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* |
96
|
|
|
* Sets the table to insert into. |
97
|
|
|
* |
98
|
|
|
* @param string $into The table to insert into. |
99
|
|
|
* |
100
|
|
|
* @return $this |
101
|
|
|
* |
102
|
|
|
*/ |
103
|
56 |
|
public function into($into) |
104
|
|
|
{ |
105
|
|
|
// don't quote yet, we might need it for getLastInsertIdName() |
106
|
56 |
|
$this->into = $into; |
107
|
56 |
|
return $this; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* |
112
|
|
|
* Builds this query object into a string. |
113
|
|
|
* |
114
|
|
|
* @return string |
115
|
|
|
* |
116
|
|
|
*/ |
117
|
30 |
|
protected function build() |
118
|
|
|
{ |
119
|
|
|
return 'INSERT' |
120
|
30 |
|
. $this->buildFlags() |
121
|
30 |
|
. $this->buildInto() |
122
|
30 |
|
. $this->buildValuesForInsert() |
123
|
30 |
|
. $this->buildReturning(); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* |
128
|
|
|
* Builds the INTO clause. |
129
|
|
|
* |
130
|
|
|
* @return string |
131
|
|
|
* |
132
|
|
|
*/ |
133
|
41 |
|
protected function buildInto() |
134
|
|
|
{ |
135
|
41 |
|
return " INTO " . $this->quoter->quoteName($this->into); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* |
140
|
|
|
* Returns the proper name for passing to `PDO::lastInsertId()`. |
141
|
|
|
* |
142
|
|
|
* @param string $col The last insert ID column. |
143
|
|
|
* |
144
|
|
|
* @return mixed Normally null, since most drivers do not need a name; |
145
|
|
|
* alternatively, a string from `$last_insert_id_names`. |
146
|
|
|
* |
147
|
|
|
*/ |
148
|
10 |
|
public function getLastInsertIdName($col) |
149
|
|
|
{ |
150
|
10 |
|
$key = $this->into . '.' . $col; |
151
|
10 |
|
if (isset($this->last_insert_id_names[$key])) { |
152
|
5 |
|
return $this->last_insert_id_names[$key]; |
153
|
|
|
} |
154
|
5 |
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* |
158
|
|
|
* Sets one column value placeholder; if an optional second parameter is |
159
|
|
|
* passed, that value is bound to the placeholder. |
160
|
|
|
* |
161
|
|
|
* @param string $col The column name. |
162
|
|
|
* |
163
|
|
|
* @param array $value Optional: a value to bind to the placeholder. |
164
|
|
|
* |
165
|
|
|
* @return $this |
166
|
|
|
* |
167
|
|
|
*/ |
168
|
20 |
|
public function col($col, ...$value) |
169
|
|
|
{ |
170
|
20 |
|
return $this->addCol($col, ...$value); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* |
175
|
|
|
* Sets multiple column value placeholders. If an element is a key-value |
176
|
|
|
* pair, the key is treated as the column name and the value is bound to |
177
|
|
|
* that column. |
178
|
|
|
* |
179
|
|
|
* @param array $cols A list of column names, optionally as key-value |
180
|
|
|
* pairs where the key is a column name and the value is a bind value for |
181
|
|
|
* that column. |
182
|
|
|
* |
183
|
|
|
* @return $this |
184
|
|
|
* |
185
|
|
|
*/ |
186
|
46 |
|
public function cols(array $cols) |
187
|
|
|
{ |
188
|
46 |
|
return $this->addCols($cols); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* |
193
|
|
|
* Sets a column value directly; the value will not be escaped, although |
194
|
|
|
* fully-qualified identifiers in the value will be quoted. |
195
|
|
|
* |
196
|
|
|
* @param string $col The column name. |
197
|
|
|
* |
198
|
|
|
* @param string $value The column value expression. |
199
|
|
|
* |
200
|
|
|
* @return $this |
201
|
|
|
* |
202
|
|
|
*/ |
203
|
31 |
|
public function set($col, $value) |
204
|
|
|
{ |
205
|
31 |
|
return $this->setCol($col, $value); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* |
210
|
|
|
* Gets the values to bind to placeholders. |
211
|
|
|
* |
212
|
|
|
* @return array |
213
|
|
|
* |
214
|
|
|
*/ |
215
|
31 |
|
public function getBindValues() |
216
|
|
|
{ |
217
|
31 |
|
return array_merge(parent::getBindValues(), $this->bind_values_bulk); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* |
222
|
|
|
* Adds multiple rows for bulk insert. |
223
|
|
|
* |
224
|
|
|
* @param array $rows An array of rows, where each element is an array of |
225
|
|
|
* column key-value pairs. The values are bound to placeholders. |
226
|
|
|
* |
227
|
|
|
* @return $this |
228
|
|
|
* |
229
|
|
|
*/ |
230
|
15 |
|
public function addRows(array $rows) |
231
|
|
|
{ |
232
|
15 |
|
foreach ($rows as $cols) { |
233
|
15 |
|
$this->addRow($cols); |
234
|
15 |
|
} |
235
|
15 |
|
if ($this->row > 1) { |
236
|
10 |
|
$this->finishRow(); |
237
|
10 |
|
} |
238
|
15 |
|
return $this; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* |
243
|
|
|
* Add one row for bulk insert; increments the row counter and optionally |
244
|
|
|
* adds columns to the new row. |
245
|
|
|
* |
246
|
|
|
* When adding the first row, the counter is not incremented. |
247
|
|
|
* |
248
|
|
|
* After calling `addRow()`, you can further call `col()`, `cols()`, and |
249
|
|
|
* `set()` to work with the newly-added row. Calling `addRow()` again will |
250
|
|
|
* finish off the current row and start a new one. |
251
|
|
|
* |
252
|
|
|
* @param array $cols An array of column key-value pairs; the values are |
253
|
|
|
* bound to placeholders. |
254
|
|
|
* |
255
|
|
|
* @return $this |
256
|
|
|
* |
257
|
|
|
*/ |
258
|
30 |
|
public function addRow(array $cols = array()) |
259
|
|
|
{ |
260
|
30 |
|
if (! $this->col_values) { |
|
|
|
|
261
|
15 |
|
return $this->cols($cols); |
262
|
|
|
} |
263
|
|
|
|
264
|
25 |
|
if (! $this->col_order) { |
|
|
|
|
265
|
25 |
|
$this->col_order = array_keys($this->col_values); |
266
|
25 |
|
} |
267
|
|
|
|
268
|
25 |
|
$this->finishRow(); |
269
|
25 |
|
$this->row ++; |
270
|
25 |
|
$this->cols($cols); |
271
|
25 |
|
return $this; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* |
276
|
|
|
* Finishes off the current row in a bulk insert, collecting the bulk |
277
|
|
|
* values and resetting for the next row. |
278
|
|
|
* |
279
|
|
|
* @return null |
280
|
|
|
* |
281
|
|
|
*/ |
282
|
25 |
|
protected function finishRow() |
283
|
|
|
{ |
284
|
25 |
|
if (! $this->col_values) { |
|
|
|
|
285
|
15 |
|
return; |
286
|
|
|
} |
287
|
|
|
|
288
|
25 |
|
foreach ($this->col_order as $col) { |
289
|
25 |
|
$this->finishCol($col); |
290
|
25 |
|
} |
291
|
|
|
|
292
|
25 |
|
$this->col_values = array(); |
293
|
25 |
|
$this->bind_values = array(); |
294
|
25 |
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* |
298
|
|
|
* Finishes off a single column of the current row in a bulk insert. |
299
|
|
|
* |
300
|
|
|
* @param string $col The column to finish off. |
301
|
|
|
* |
302
|
|
|
* @return null |
303
|
|
|
* |
304
|
|
|
* @throws Exception on named column missing from row. |
305
|
|
|
* |
306
|
|
|
*/ |
307
|
25 |
|
protected function finishCol($col) |
308
|
|
|
{ |
309
|
25 |
|
if (! array_key_exists($col, $this->col_values)) { |
310
|
5 |
|
throw new Exception("Column $col missing from row {$this->row}."); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
// get the current col_value |
314
|
25 |
|
$value = $this->col_values[$col]; |
315
|
|
|
|
316
|
|
|
// is it *not* a placeholder? |
317
|
25 |
|
if (substr($value, 0, 1) != ':') { |
318
|
|
|
// copy the value as-is |
319
|
15 |
|
$this->col_values_bulk[$this->row][$col] = $value; |
320
|
15 |
|
return; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
// retain col_values in bulk with the row number appended |
324
|
25 |
|
$this->col_values_bulk[$this->row][$col] = "{$value}_{$this->row}"; |
325
|
|
|
|
326
|
|
|
// the existing placeholder name without : or row number |
327
|
25 |
|
$name = substr($value, 1); |
328
|
|
|
|
329
|
|
|
// retain bind_value in bulk with new placeholder |
330
|
25 |
|
if (array_key_exists($name, $this->bind_values)) { |
331
|
25 |
|
$this->bind_values_bulk["{$name}_{$this->row}"] = $this->bind_values[$name]; |
332
|
25 |
|
} |
333
|
25 |
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* |
337
|
|
|
* Builds the inserted columns and values of the statement. |
338
|
|
|
* |
339
|
|
|
* @return string |
340
|
|
|
* |
341
|
|
|
*/ |
342
|
41 |
|
protected function buildValuesForInsert() |
343
|
|
|
{ |
344
|
41 |
|
if ($this->row) { |
345
|
20 |
|
return $this->buildValuesForBulkInsert(); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
return ' (' |
349
|
21 |
|
. $this->indentCsv(array_keys($this->col_values)) |
350
|
21 |
|
. PHP_EOL . ') VALUES (' |
351
|
21 |
|
. $this->indentCsv(array_values($this->col_values)) |
352
|
21 |
|
. PHP_EOL . ')'; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* |
357
|
|
|
* Builds the bulk-inserted columns and values of the statement. |
358
|
|
|
* |
359
|
|
|
* @return string |
360
|
|
|
* |
361
|
|
|
*/ |
362
|
20 |
|
protected function buildValuesForBulkInsert() |
363
|
|
|
{ |
364
|
20 |
|
$this->finishRow(); |
365
|
20 |
|
$cols = " (" . implode(', ', $this->col_order) . ")"; |
366
|
20 |
|
$vals = array(); |
367
|
20 |
|
foreach ($this->col_values_bulk as $row_values) { |
368
|
20 |
|
$vals[] = " (" . implode(', ', $row_values) . ")"; |
369
|
20 |
|
} |
370
|
20 |
|
return PHP_EOL . $cols . PHP_EOL |
371
|
20 |
|
. "VALUES" . PHP_EOL |
372
|
20 |
|
. implode("," . PHP_EOL, $vals); |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.