Simpletest Coverage - modules/field/modules/field_sql_storage/field_sql_storage.module

1 <?php
2 // $Id: field_sql_storage.module,v 1.18 2009/08/11 14:59:40 dries Exp $
3
4 /**
5 * @file
6 * Default implementation of the field storage API.
7 */
8
9 /**
10 * Implement hook_help().
11 */
12 function field_sql_storage_help($path, $arg) {
13 switch ($path) {
14 case 'admin/help#field_sql_storage':
15 $output = '<p>' . t('The Field SQL Storage module stores Field API data in the database. It is the default field storage module, but other field storage modules may be available in the contributions repository.') . '</p>';
16 return $output;
17 }
18 }
19
20 /**
21 * Generate a table name for a field data table.
22 *
23 * @param $field
24 * The field structure.
25 * @return
26 * A string containing the generated name for the database table
27 */
28 function _field_sql_storage_tablename($field) {
29 return "field_data_{$field['field_name']}_{$field['id']}";
30 }
31
32 /**
33 * Generate a table name for a field revision archive table.
34 *
35 * @param $name
36 * The field structure.
37 * @return
38 * A string containing the generated name for the database table
39 */
40 function _field_sql_storage_revision_tablename($field) {
41 return "field_revision_{$field['field_name']}_{$field['id']}";
42 }
43
44 /**
45 * Generate a column name for a field data table.
46 *
47 * @param $name
48 * The name of the field
49 * @param $column
50 * The name of the column
51 * @return
52 * A string containing a generated column name for a field data
53 * table that is unique among all other fields.
54 */
55 function _field_sql_storage_columnname($name, $column) {
56 return $name . '_' . $column;
57 }
58
59 /**
60 * Generate an index name for a field data table.
61 *
62 * @param $name
63 * The name of the field
64 * @param $column
65 * The name of the index
66 * @return
67 * A string containing a generated index name for a field data
68 * table that is unique among all other fields.
69 */
70 function _field_sql_storage_indexname($name, $index) {
71 return $name . '_' . $index;
72 }
73
74 /**
75 * Retrieve or assign an entity type id for an object type.
76 *
77 * @param $obj_type
78 * The object type, such as 'node' or 'user'.
79 * @return
80 * The entity type id.
81 *
82 * TODO: We need to decide on 'entity' or 'object'.
83 */
84 function _field_sql_storage_etid($obj_type) {
85 $etid = variable_get('field_sql_storage_' . $obj_type . '_etid', NULL);
86 if (is_null($etid)) {
87 $etid = db_insert('field_config_entity_type')->fields(array('type' => $obj_type))->execute();
88 variable_set('field_sql_storage_' . $obj_type . '_etid', $etid);
89 }
90 return $etid;
91 }
92
93 /**
94 * Return the database schema for a field. This may contain one or
95 * more tables. Each table will contain the columns relevant for the
96 * specified field. Leave the $field's 'columns' and 'indexes' keys
97 * empty to get only the base schema.
98 *
99 * @param $field
100 * The field structure for which to generate a database schema.
101 * @return
102 * One or more tables representing the schema for the field.
103 */
104 function _field_sql_storage_schema($field) {
105 $deleted = $field['deleted'] ? 'deleted ' : '';
106 $current = array(
107 'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})",
108 'fields' => array(
109 'etid' => array(
110 'type' => 'int',
111 'unsigned' => TRUE,
112 'not null' => TRUE,
113 'description' => 'The entity type id this data is attached to',
114 ),
115 'bundle' => array(
116 'type' => 'varchar',
117 'length' => 32,
118 'not null' => TRUE,
119 'default' => '',
120 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
121 ),
122 'deleted' => array(
123 'type' => 'int',
124 'size' => 'tiny',
125 'not null' => TRUE,
126 'default' => 0,
127 'description' => 'A boolean indicating whether this data item has been deleted'
128 ),
129 'entity_id' => array(
130 'type' => 'int',
131 'unsigned' => TRUE,
132 'not null' => TRUE,
133 'description' => 'The entity id this data is attached to',
134 ),
135 'revision_id' => array(
136 'type' => 'int',
137 'unsigned' => TRUE,
138 'not null' => FALSE,
139 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
140 ),
141 'delta' => array(
142 'type' => 'int',
143 'unsigned' => TRUE,
144 'not null' => TRUE,
145 'description' => 'The sequence number for this data item, used for multi-value fields',
146 ),
147 ),
148 'primary key' => array('etid', 'entity_id', 'deleted', 'delta'),
149 // TODO : index on 'bundle'
150 );
151
152 // Add field columns.
153 foreach ((array) $field['columns'] as $column_name => $attributes) {
154 $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
155 $current['fields'][$real_name] = $attributes;
156 }
157
158 // Add indexes.
159 foreach ((array) $field['indexes'] as $index_name => $columns) {
160 $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
161 foreach ($columns as $column_name) {
162 $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
163 }
164 }
165
166 // Construct the revision table. The primary key includes
167 // revision_id but not entity_id so that multiple revision loads can
168 // use the IN operator.
169 $revision = $current;
170 $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})";
171 $revision['revision_id']['description'] = 'The entity revision id this data is attached to';
172 $revision['primary key'] = array('etid', 'revision_id', 'deleted', 'delta');
173
174 return array(
175 _field_sql_storage_tablename($field) => $current,
176 _field_sql_storage_revision_tablename($field) => $revision,
177 );
178 }
179
180 /**
181 * Implement hook_field_storage_create_field().
182 */
183 function field_sql_storage_field_storage_create_field($field) {
184 $schema = _field_sql_storage_schema($field);
185 foreach ($schema as $name => $table) {
186 db_create_table($ret, $name, $table);
187 }
188 }
189
190 /**
191 * Implement hook_field_storage_delete_field().
192 */
193 function field_sql_storage_field_storage_delete_field($field_name) {
194 // Mark all data associated with the field for deletion.
195 $field = field_info_field($field_name);
196 $table = _field_sql_storage_tablename($field);
197 db_update($table)
198 ->fields(array('deleted' => 1))
199 ->execute();
200 }
201
202 /**
203 * Implement hook_field_storage_load().
204 */
205 function field_sql_storage_field_storage_load($obj_type, $objects, $age, $skip_fields, $options) {
206 $etid = _field_sql_storage_etid($obj_type);
207 $load_current = $age == FIELD_LOAD_CURRENT;
208
209 // Gather ids needed for each field.
210 $field_ids = array();
211 $delta_count = array();
212 foreach ($objects as $obj) {
213 list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $obj);
214
215 if ($options['deleted']) {
216 $instances = field_read_instances(array('bundle' => $bundle), array('include_deleted' => $options['deleted']));
217 }
218 else {
219 $instances = field_info_instances($bundle);
220 }
221
222 foreach ($instances as $instance) {
223 $field_name = $instance['field_name'];
224 if (!isset($skip_fields[$instance['field_id']]) && (!isset($options['field_id']) || $options['field_id'] == $instance['field_id'])) {
225 $objects[$id]->{$field_name} = array();
226 $field_ids[$instance['field_id']][] = $load_current ? $id : $vid;
227 $delta_count[$id][$field_name] = 0;
228 }
229 }
230 }
231
232 foreach ($field_ids as $field_id => $ids) {
233 $field = field_info_field_by_id($field_id);
234 $field_name = $field['field_name'];
235 $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
236
237 $query = db_select($table, 't')
238 ->fields('t')
239 ->condition('etid', $etid)
240 ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
241 ->orderBy('delta');
242
243 if (empty($options['deleted'])) {
244 $query->condition('deleted', 0);
245 }
246
247 $results = $query->execute();
248
249 foreach ($results as $row) {
250 if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$field_name] < $field['cardinality']) {
251 $item = array();
252 // For each column declared by the field, populate the item
253 // from the prefixed database column.
254 foreach ($field['columns'] as $column => $attributes) {
255 $column_name = _field_sql_storage_columnname($field_name, $column);
256 $item[$column] = $row->$column_name;
257 }
258
259 // Add the item to the field values for the entity.
260 $objects[$row->entity_id]->{$field_name}[] = $item;
261 $delta_count[$row->entity_id][$field_name]++;
262 }
263 }
264 }
265 }
266
267 /**
268 * Implement hook_field_storage_write().
269 */
270 function field_sql_storage_field_storage_write($obj_type, $object, $op, $skip_fields) {
271 list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
272 $etid = _field_sql_storage_etid($obj_type);
273
274 $instances = field_info_instances($bundle);
275 foreach ($instances as $instance) {
276 $field_name = $instance['field_name'];
277 if (isset($skip_fields[$instance['field_id']])) {
278 continue;
279 }
280
281 $field = field_info_field($field_name);
282 $table_name = _field_sql_storage_tablename($field);
283 $revision_name = _field_sql_storage_revision_tablename($field);
284
285 // Leave the field untouched if $object comes with no $field_name property.
286 // Empty the field if $object->$field_name is NULL or an empty array.
287
288 // Function property_exists() is slower, so we catch the more frequent cases
289 // where it's an empty array with the faster isset().
290 if (isset($object->$field_name) || property_exists($object, $field_name)) {
291 // Delete and insert, rather than update, in case a value was added.
292 if ($op == FIELD_STORAGE_UPDATE) {
293 db_delete($table_name)->condition('etid', $etid)->condition('entity_id', $id)->execute();
294 if (isset($vid)) {
295 db_delete($revision_name)->condition('etid', $etid)->condition('entity_id', $id)->condition('revision_id', $vid)->execute();
296 }
297 }
298
299 if ($object->$field_name) {
300 // Prepare the multi-insert query.
301 $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta');
302 foreach ($field['columns'] as $column => $attributes) {
303 $columns[] = _field_sql_storage_columnname($field_name, $column);
304 }
305 $query = db_insert($table_name)->fields($columns);
306 if (isset($vid)) {
307 $revision_query = db_insert($revision_name)->fields($columns);
308 }
309
310 $delta_count = 0;
311 foreach ($object->$field_name as $delta => $item) {
312 $record = array(
313 'etid' => $etid,
314 'entity_id' => $id,
315 'revision_id' => $vid,
316 'bundle' => $bundle,
317 'delta' => $delta,
318 );
319 foreach ($field['columns'] as $column => $attributes) {
320 $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
321 }
322 $query->values($record);
323 if (isset($vid)) {
324 $revision_query->values($record);
325 }
326
327 if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
328 break;
329 }
330 }
331
332 // Execute the insert.
333 $query->execute();
334 if (isset($vid)) {
335 $revision_query->execute();
336 }
337 }
338 }
339 }
340 }
341
342 /**
343 * Implement hook_field_storage_delete().
344 *
345 * This function deletes data for all fields for an object from the database.
346 */
347 function field_sql_storage_field_storage_delete($obj_type, $object) {
348 list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
349 $etid = _field_sql_storage_etid($obj_type);
350
351 $instances = field_info_instances($bundle);
352 foreach ($instances as $instance) {
353 $field = field_info_field($instance['field_name']);
354 field_sql_storage_field_storage_purge($obj_type, $object, $field, $instance);
355 }
356 }
357
358 /**
359 * Implement hook_field_storage_purge().
360 *
361 * This function deletes data from the database for a single field on
362 * an object.
363 */
364 function field_sql_storage_field_storage_purge($obj_type, $object, $field, $instance) {
365 list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
366 $etid = _field_sql_storage_etid($obj_type);
367
368 $field = field_info_field_by_id($field['id']);
369 $table_name = _field_sql_storage_tablename($field);
370 $revision_name = _field_sql_storage_revision_tablename($field);
371 db_delete($table_name)
372 ->condition('etid', $etid)
373 ->condition('entity_id', $id)
374 ->execute();
375 db_delete($revision_name)
376 ->condition('etid', $etid)
377 ->condition('entity_id', $id)
378 ->execute();
379 }
380
381 /**
382 * Implement hook_field_storage_query().
383 */
384 function field_sql_storage_field_storage_query($field_id, $conditions, $count, &$cursor, $age) {
385 $load_current = $age == FIELD_LOAD_CURRENT;
386
387 $field = field_info_field_by_id($field_id);
388 $field_name = $field['field_name'];
389 $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
390 $field_columns = array_keys($field['columns']);
391
392 // Build the query.
393 $query = db_select($table, 't');
394 $query->join('field_config_entity_type', 'e', 't.etid = e.etid');
395
396 $query
397 ->fields('t', array('bundle', 'entity_id', 'revision_id'))
398 ->fields('e', array('type'))
399 // We need to ensure objects arrive in a consistent order for the
400 // range() operation to work.
401 ->orderBy('t.etid')
402 ->orderBy('t.entity_id');
403
404 // Add conditions.
405 foreach ($conditions as $condition) {
406 // A condition is either a (column, value, operator) triple, or a
407 // (column, value) pair with implied operator.
408 @list($column, $value, $operator) = $condition;
409 // Translate operator and value if needed.
410 switch ($operator) {
411 case 'STARTS_WITH':
412 $operator = 'LIKE';
413 $value .= '%';
414 break;
415
416 case 'ENDS_WITH':
417 $operator = 'LIKE';
418 $value = "$value%";
419 break;
420
421 case 'CONTAINS':
422 $operator = 'LIKE';
423 $value = "%$value%";
424 break;
425 }
426 // Translate field columns into prefixed db columns.
427 if (in_array($column, $field_columns)) {
428 $column = _field_sql_storage_columnname($field_name, $column);
429 }
430 $query->condition($column, $value, $operator);
431
432 if ($column == 'deleted') {
433 $deleted = $value;
434 }
435 }
436
437 // Exclude deleted data unless we have a condition on it.
438 if (!isset($deleted)) {
439 $query->condition('deleted', 0);
440 }
441
442 // Initialize results array
443 $return = array();
444
445 // Getting $count objects possibly requires reading more than $count rows
446 // since fields with multiple values span over several rows. We query for
447 // batches of $count rows until we've either read $count objects or received
448 // less rows than asked for.
449 $obj_count = 0;
450 do {
451 if ($count != FIELD_QUERY_NO_LIMIT) {
452 $query->range($cursor, $count);
453 }
454 $results = $query->execute();
455
456 $row_count = 0;
457 foreach ($results as $row) {
458 $row_count++;
459 $cursor++;
460 // If querying all revisions and the entity type has revisions, we need
461 // to key the results by revision_ids.
462 $entity_type = field_info_fieldable_types($row->type);
463 $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id;
464
465 if (!isset($return[$row->type][$id])) {
466 $return[$row->type][$id] = field_attach_create_stub_object($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
467 $obj_count++;
468 }
469 }
470 } while ($count != FIELD_QUERY_NO_LIMIT && $row_count == $count && $obj_count < $count);
471
472 // The query is complete when the last batch returns less rows than asked
473 // for.
474 if ($row_count < $count) {
475 $cursor = FIELD_QUERY_COMPLETE;
476 }
477
478 return $return;
479 }
480
481 /**
482 * Implement hook_field_storage_delete_revision().
483 *
484 * This function actually deletes the data from the database.
485 */
486 function field_sql_storage_field_storage_delete_revision($obj_type, $object) {
487 list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
488 $etid = _field_sql_storage_etid($obj_type);
489
490 if (isset($vid)) {
491 $instances = field_info_instances($bundle);
492 foreach ($instances as $instance) {
493 $field_name = $instance['field_name'];
494 $field = field_read_field($field_name);
495 $revision_name = _field_sql_storage_revision_tablename($field);
496 db_delete($revision_name)
497 ->condition('etid', $etid)
498 ->condition('entity_id', $id)
499 ->condition('revision_id', $vid)
500 ->execute();
501 }
502 }
503 }
504
505 /**
506 * Implement hook_field_storage_delete_instance().
507 *
508 * This function simply marks for deletion all data associated with the field.
509 */
510 function field_sql_storage_field_storage_delete_instance($field_name, $bundle) {
511 $field = field_read_field($field_name);
512 $table_name = _field_sql_storage_tablename($field);
513 $revision_name = _field_sql_storage_revision_tablename($field);
514 db_update($table_name)
515 ->fields(array('deleted' => 1))
516 ->condition('bundle', $bundle)
517 ->execute();
518 db_update($revision_name)
519 ->fields(array('deleted' => 1))
520 ->condition('bundle', $bundle)
521 ->execute();
522 }
523
524 /**
525 * Implement hook_field_storage_rename_bundle().
526 */
527 function field_sql_storage_field_storage_rename_bundle($bundle_old, $bundle_new) {
528 $instances = field_info_instances($bundle_old);
529 foreach ($instances as $instance) {
530 $field = field_read_field($instance['field_name']);
531 $table_name = _field_sql_storage_tablename($field);
532 $revision_name = _field_sql_storage_revision_tablename($field);
533 db_update($table_name)
534 ->fields(array('bundle' => $bundle_new))
535 ->condition('bundle', $bundle_old)
536 ->execute();
537 db_update($revision_name)
538 ->fields(array('bundle' => $bundle_new))
539 ->condition('bundle', $bundle_old)
540 ->execute();
541 }
542 }
543
544 /**
545 * Implement hook_field_storage_purge_field().
546 *
547 * All field data items and instances have already been purged, so all
548 * that is left is to delete the table.
549 */
550 function field_sql_storage_field_storage_purge_field($field) {
551 $ret = array();
552 $table_name = _field_sql_storage_tablename($field);
553 $revision_name = _field_sql_storage_revision_tablename($field);
554 db_drop_table($ret, $table_name);
555 db_drop_table($ret, $revision_name);
556 }
557
558

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.