logfile = fopen($logfile, 'w') or die("Can't open logfile '$logfile'.");
} else {
// Assume it's an open file resource
$this->logfile = $logfile;
}
}
// By default, print out all statements executed if and only if SQL_DEBUG is defined as a true value.
$this->echo = $echo;
}
function logline($msg) {
if ($this->echo) {
echo (is_string($this->echo)? $this->echo: "\n
");
echo htmlentities($msg);
//echo $msg;
}
if ($this->logfile) {
$find = array("\r", "\t", "\n\n");
$repl = array('', ' ', "\n");
fwrite($this->logfile, trim(str_replace($find, $repl, $msg))."\n\n");
}
}
function warn($msg, $fatal = false) {
$this->logline('-- WARNING: '.$msg);
$this->warnings[] = $this->warning = $msg;
if ($fatal) exit(1);
}
function sum_query($result_field, $fields, $tables, $conditions = null, $allow_deleted = false) {
if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);
$query = 'SELECT SUM(' . $this->escape($fields) . ') AS ' . $this->escape($result_field) .
$this->from($tables, $allow_deleted) . $this->conditions($conditions);
return $query;
}
function count_query($result_field, $fields, $tables, $conditions = null, $allow_deleted = false) {
if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);
$query = 'SELECT COUNT(' . $this->escape($fields) . ') AS ' . $this->escape($result_field) .
$this->from($tables, $allow_deleted) . $this->conditions($conditions);
return $query;
}
function select_query($fields, $tables, $conditions = null, $order = null, $group = null, $limit = null, $page = 1, $allow_deleted = false) {
$order_clause = $this->order_clause($order);
$group_clause = $group? ' GROUP BY ' . $this->escape($group): '';
if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($tables), $conditions);
$skip = (intval($page) > 1)? (intval($limit) * (intval($page) - 1)).', ': '';
$limit_clause = intval($limit)? ' LIMIT '.$skip.intval($limit): '';
$query = 'SELECT ' . $this->escape($fields) . $this->from($tables, $allow_deleted) .
$this->conditions($conditions) . $group_clause . $order_clause . $limit_clause;
return $query;
}
function insert_query($table, $fields) {
if ($this->common_fields) {
// Automatically insert common fields
$fields['creator_id'] = $this->user_id;
$fields['date_created'] = $this->datetime();
}
$query = 'INSERT INTO ' . $table . $this->conditions($fields, ' SET ', ', ');
return $query;
}
function update_query($table, $updates, $conditions, $allow_deleted = false) {
if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($table), $conditions);
$query = 'UPDATE ' . $this->escape($table) . $this->conditions($updates, ' SET ', ', ') . $this->conditions($conditions);
return $query;
}
function delete_query($table, $conditions) {
// Actually removes the rows from the database.
$query = 'DELETE FROM ' . $this->escape($table) . $this->conditions($conditions);
return $query;
}
function create_table_query($table, $fields, $keys = array(), $engine = 'InnoDB', $charset = 'utf8') {
// Constructs a query to create the table.
//
// $fields should be an array of 'column_name' => array(options), where options can be:
// * 'reference': 'table.field'; specifies that this is a foreign key to the given table.
// * 'type': Either 'primary', or a valid MySQL type. Required unless 'reference' is given.
// * 'signed': For numeric types, specifies whether the field is signed (true) or unsigned (false).
// * 'length': An optional length for string column types, and for years (2 or 4).
// * 'precision': Number of total digits for fixed-point decimal column types. Defaults to 10.
// * 'scale': Number of digits after the decimal point for fixed-point column types. Defaults to 2.
// * 'values': A required array of values for 'set' and 'enum' column types.
// * 'null': Whether to allow null values. Defaults to NOT NULL, unless 'default' is null.
// * 'default': Default value for the column. If null, overrides the 'null' option.
// * 'unique': Whether to disallow duplicate values in this column. Defaults to false.
//
// $keys is an array of arrays, where each array is a list of column names in the key.
// Each array may also contain the key 'unique', with the same meaning as above.
//
// $engine and $charset should usually not be specified.
$common_fields = array(
'id' => array('type' => 'primary'),
'sequence' => array('type' => 'int', 'signed' => true, 'default' => 1),
'creator_id' => array('reference' => 'users.id', 'default' => null),
'date_created' => array('type' => 'datetime', 'default' => null),
'date_deleted' => array('type' => 'datetime', 'default' => null),
'description_id' => array('reference' => 'descriptions.id', 'default' => null),
);
$lines = array();
$indexes = array();
$references = array();
if (!is_array($keys)) $keys = array();
foreach (array_merge($common_fields, $fields) as $column => $field) {
$signed = $null = $default = null;
$items = array($this->escape($column));
if (array_key_exists('unique', $field) and !empty($field['unique'])) {
$keys[] = array($column, 'unique' => true);
}
if (array_key_exists('default', $field)) {
$default = 'DEFAULT '.$this->sanitize($field['default']);
if (!is_null($field['default']) and !array_key_exists('null', $field)) $null = 'NOT NULL';
} else if (empty($field['null'])) {
// Note: This makes each field not null unless otherwise specified.
$null = 'NOT NULL';
}
if (!empty($field['reference'])) {
$type = 'INT';
$signed = 'UNSIGNED';
$references[$column] = $field['reference'];
}
if (!empty($field['type'])) {
switch ($field['type']) {
// Aliases to set several attributes at once
case 'primary':
$type = 'INT'; // Todo: Consider bigint for primary keys
$null = 'NOT NULL';
$signed = 'UNSIGNED';
$default = 'AUTO_INCREMENT PRIMARY KEY';
break;
case 'serial':
$type = 'BIGINT';
$null = 'NOT NULL';
$signed = 'UNSIGNED';
$default = 'AUTO_INCREMENT UNIQUE';
break;
// Numeric types
case 'int':
case 'float':
case 'double':
case 'bigint':
case 'integer':
case 'tinyint':
case 'smallint':
case 'mediumint':
$type = strtoupper($field['type']);
if (array_key_exists('signed', $field)) $signed = $field['signed']? 'SIGNED': 'UNSIGNED';
break;
case 'bool':
case 'boolean':
if (array_key_exists('default', $field)) {
$val = is_null($field['default'])? 'NULL': empty($field['default'])? 'FALSE': 'TRUE';
$default = 'DEFAULT '.$val;
}
// Fall through
case 'bit':
$type = strtoupper($field['type']);
//$signed = 'UNSIGNED';
break;
case 'dec':
case 'fixed':
case 'decimal':
if (array_key_exists('signed', $field)) $signed = $field['signed']? 'SIGNED': 'UNSIGNED';
$precision = array_key_exists('precision', $field)? intval($field['precision']): 10;
$scale = array_key_exists('scale', $field)? intval($field['scale']): 2;
$type = "DECIMAL($precision,$scale)";
break;
// Date and time types
case 'year':
$length = array_key_exists('length', $field)? intval($field['length']): 4;
$type = "YEAR($length)";
break;
case 'date':
case 'time':
case 'datetime':
case 'timestamp':
$type = strtoupper($field['type']);
break;
// String types
case 'char':
case 'blob':
case 'text':
case 'binary':
case 'varchar':
case 'tinyblob':
case 'tinytext':
case 'longblob':
case 'longtext':
case 'varbinary':
case 'mediumblob':
case 'mediumtext':
$type = strtoupper($field['type']);
if (array_key_exists('length', $field)) $type .= '('.intval($field['length']).')';
break;
case 'set':
case 'enum':
$values = array();
if (array_key_exists('values', $field) and is_array($field['values'])) {
foreach ($field['values'] as $val) $values[] = $this->sanitize($val);
} else {
$this->warn("Missing $field[type] values for column $column.");
}
$type = strtoupper($field['type']).'('.join(',', $values).')';
break;
default:
$this->warn("Unsupported type '$field[type]' for column $column.");
continue 2;
}
} else if (empty($type)) {
$this->warn("Missing type for column $column.");
continue;
}
$items[] = $type;
if (!empty($signed)) $items[] = $signed;
if (!empty($null)) $items[] = $null;
if (!empty($default)) $items[] = $default;
$lines[] = join(' ', $items);
}
foreach ($keys as $columns) {
if (!is_array($columns)) $columns = array($columns);
$unique = '';
if (array_key_exists('unique', $columns)) {
if (empty($columns['unique'])) {
unset($columns['unique']);
} else {
$columns['unique'] = 'date_deleted';
$unique = 'UNIQUE ';
}
}
$indexes[$columns[0]] = true;
$lines[] = $unique.'KEY ('.join(',', array_map(array($this, 'escape'), $columns)).')';
}
foreach ($references as $column => $ref) {
$col = $this->escape($column);
$info = explode('.', $ref);
switch (count($info)) {
case 1:
$reftab = $this->escape($ref);
$refcol = $this->escape('id');
break;
case 2:
$reftab = $this->escape($info[0]);
$refcol = $this->escape($info[1]);
break;
default:
$this->warn("Unparsable reference '$ref' in column $column.");
continue 2;
}
$cn = $this->escape($table.'__'.$column);
$key = array_key_exists($column, $indexes)? '': "KEY ($col), ";
$lines[] = $key."CONSTRAINT $cn FOREIGN KEY ($col) REFERENCES $reftab ($refcol)";
}
$query = "CREATE TABLE ".$this->escape($table)." (\n ".join(",\n ", $lines)."\n) ENGINE=$engine DEFAULT CHARSET=$charset";
return $query;
}
function is_list($array) {
foreach (array_keys($array) as $key) if (!is_int($key)) return false;
return true;
}
function conditions($fields, $clause = ' WHERE ', $join = ' AND ') {
$inserting = (trim($clause) == 'SET');
$joining = (trim($clause) == 'ON');
if (empty($fields)) {
if ($inserting) $this->warn('Trying to update nothing');
return '';
}
$results = array();
$operator = $inserting? '=': 'IS';
foreach ($fields as $key => $value) {
if (is_null($value)) {
$results[] = $this->escape($key)." $operator NULL";
} else if (is_array($value)) {
if (empty($value)) {
$results[] = 'FALSE';
} else if (is_numeric($key)) {
$results[] = $this->conditions($value, '', (trim($join) == 'AND')? ' OR ': ' AND ');
} else if ($this->is_list($value)) {
$vals = array();
$has_null = false;
foreach ($value as $item) {
if (is_null($item)) {
$has_null = true;
} else {
$vals[] = $this->sanitize($item);
}
}
$column = $this->escape($key);
switch (count($vals)) {
case 0: $main = 'FALSE'; break;
case 1: $main = $column.' = '.$vals[0]; break;
default: $main = $column.' IN ('.join(', ', $vals).')'; break;
}
$results[] = $has_null? "($column IS NULL) OR ($main)": $main;
} else {
$results[] = $this->conditions($value, '', ' '.trim(strval($key)).' ');
}
} else if (is_numeric($key)) {
$results[] = $value;
} else if ($joining and is_string($value) and preg_match("/^[a-z]\\w+\\.\\w+$/", $value)) {
$results[] = $this->escape($key).' <=> '.$this->escape($value);
} else {
$results[] = $this->escape($key).' = '.$this->sanitize($value);
}
}
if (!$inserting) $results = array_map(array($this, 'wrap'), $results);
return $clause . implode($join, $results);
}
function wrap($item) {
return "($item)";
}
function from($tables, $allow_deleted = false) {
$primary = '';
$joining = '';
if (is_array($tables)) {
foreach ($tables as $table => $conditions) {
if (is_array($conditions)) {
if (!$allow_deleted) $conditions = $this->check_deleted($table, $conditions);
if (array_key_exists('on', $conditions)) {
$table = array(strval($conditions['on']) => $table);
unset($conditions['on']);
}
$joining .= ' LEFT JOIN ' . $this->escape($table) . $this->conditions($conditions, ' ON ');
} else {
$primary = $this->escape(is_numeric($table)? strval($conditions): array(strval($conditions) => $table));
}
}
} else {
$primary = $this->escape(strval($tables));
}
return ' FROM ' . $primary . $joining;
}
function primary_table($tables) {
$table = null;
if (is_string($tables)) {
$table = $tables;
} else if (is_array($tables)) {
if (array_key_exists(0, $tables)) {
$table = $tables[0];
} else {
foreach ($tables as $key => $value) {
if (is_string($value)) {
$table = $key;
break;
}
}
}
}
if (empty($table)) $this->warn('Invalid table specification: '.print_r($tables, true), true);
return $table;
}
function check_deleted($table, $conditions) {
if (!$this->common_fields) {
$result = $conditions;
} else if (is_null($conditions)) {
$result = array($table.'.date_deleted' => null);
} else if (is_array($conditions)) {
$result = $conditions;
$result[$table.'.date_deleted'] = null;
} else if (is_numeric($conditions)) {
$result = array($table.'.date_deleted' => null, $table.'.id' => intval($conditions));
} else {
$result = array($table.'.date_deleted' => null, $conditions);
}
return $result;
}
function operator($left, $op, $right, $allow_null = false) {
if (count($right) or is_null($right)) {
$result = $this->escape($left).' '.$op.' '.$this->sanitize($right);
if ($allow_null) $result = $this->conditions(array($left => null, $result), '', ' OR ');
} else if ($allow_null) {
$result = $this->conditions(array($left => null), '');
} else {
$result = 'FALSE';
}
return $result;
}
function order_clause($order) {
$clause = ' ORDER BY ';
if (empty($order)) {
$result = '';
} else if (is_array($order)) {
$escaped = array();
foreach ($order as $key => $name) {
$direction = strtoupper(trim($name));
if (in_array($direction, array('ASC', 'DESC'))) {
$escaped[] = $this->escape($key).' '.$direction;
} else {
$escaped[] = $this->escape($name);
}
}
$result = $clause . join(', ', $escaped);
} else {
$result = $clause . $this->escape($order);
}
return $result;
}
function escape($columns) {
if (empty($columns)) {
$result = '*';
} else if (is_array($columns)) {
$escaped = array();
$result = '';
foreach ($columns as $key => $name) {
if (is_numeric($key) and strtolower($name) == 'distinct') {
$result = 'DISTINCT ';
} else {
$escaped[] = (is_numeric($key)? '': $this->escape($key).' AS ').$this->escape($name);
}
}
$result .= join(', ', $escaped);
} else {
$result = '`'.str_replace('.', '`.`', $columns).'`';
}
return $result;
}
function date($timestamp = null) {
// Formats a date, defaulting to today, as expected by the MySQL server
return is_null($timestamp)? date('Y-m-d'): date('Y-m-d', $timestamp);
}
function datetime($timestamp = null) {
// Formats a time, defaulting to now, as expected by the MySQL server
return is_null($timestamp)? date('Y-m-d H:i:s'): date('Y-m-d H:i:s', $timestamp);
}
function sanitize($value) {
if (is_null($value)) {
return 'NULL';
} else if (is_int($value)) {
return intval($value);
} else if (is_numeric($value)) {
return "'$value'";
} else if ($value === false) {
return 0;
} else if ($value === true) {
return 1;
} else if (is_array($value)) {
return '('.join(',', array_map(array($this, 'sanitize'), $value)).')';
} else if ($this->db) {
return "'".mysql_real_escape_string(trim(strval($value)), $this->db)."'";
} else {
return "'".mysql_escape_string(trim(strval($value)))."'";
}
}
}
class Database extends Sql {
var $prefix = '';
function __construct($details, $logfile = null) {
// By default, print out all statements executed if and only if SQL_DEBUG is defined as a true value.
$echo = (defined('SQL_DEBUG') and SQL_DEBUG and !empty($details['database']))? "\n
$details[database]: ": false;
parent::__construct($logfile, $echo);
foreach (array('username', 'password', 'host', 'database') as $key) {
if (empty($details[$key])) $this->warn("Missing $key in connection details.", true);
}
$this->logline("-- Connecting to $details[database] on $details[username]:$details[password]@$details[host]");
$this->db = mysql_connect($details['host'], $details['username'], $details['password'], true) or die("Can't connect: " . mysql_error());
mysql_select_db($details['database'], $this->db) or die("Can't select database: " . mysql_error($this->db));
if (!empty($details['prefix'])) $this->prefix = $details['prefix'];
}
function query($query) {
$this->logline($query.';');
$results = mysql_query($query, $this->db);
if (!$results) $this->warn('Query failed: ' . mysql_error($this->db));
return $results;
}
function sum($fields, $tables, $conditions = null, $allow_deleted = false) {
$result_field = 'num';
$query = $this->sum_query($result_field, $fields, $tables, $conditions, $allow_deleted);
$results = $this->query($query);
if (!$results) return null;
$returned = mysql_num_rows($results);
$request = null;
if ($returned == 1) {
$row = mysql_fetch_assoc($results);
if (array_key_exists($result_field, $row)) {
$request = intval($row[$result_field]);
} else {
$this->warn("Can't find '$result_field' in sum results.");
}
} else {
$this->warn("$returned rows returned for a sum.");
}
return $request;
}
function count($fields, $tables, $conditions = null, $allow_deleted = false) {
$result_field = 'num';
$query = $this->count_query($result_field, $fields, $tables, $conditions, $allow_deleted);
$results = $this->query($query);
if (!$results) return null;
$returned = mysql_num_rows($results);
$request = null;
if ($returned == 1) {
$row = mysql_fetch_assoc($results);
if (array_key_exists($result_field, $row)) {
$request = intval($row[$result_field]);
$this->logline(' -- '.$request);
} else {
$this->warn("Can't find '$result_field' in count results.");
}
} else {
$this->warn("$returned rows returned for a count.");
}
return $request;
}
function select($fields, $tables, $conditions = null, $allow_deleted = false, $group = null) {
$query = $this->select_query($fields, $tables, $conditions, $order = null, $group, $limit = 1, $page = 1, $allow_deleted);
$results = $this->query($query);
if (!$results) return null;
$returned = mysql_num_rows($results);
if ($returned) {
$row = mysql_fetch_assoc($results);
$items = array();
foreach ($row as $key => $value) $items[] = $this->escape($key).' => '.$this->sanitize($value);
$this->logline(' -- ( ' . join(', ', $items) . ' )');
if (!is_string($fields)) {
$request = $row;
} else if (array_key_exists($fields, $row)) {
$request = $row[$fields];
} else if ($dot = strrpos($fields, '.')) {
$fieldname = substr($fields, $dot + 1);
if (array_key_exists($fieldname, $row)) {
$request = $row[$fieldname];
} else {
$this->warn("Can't find '$fields' or '$fieldname' in results.");
}
} else {
$this->warn("Can't find '$fields' in results.");
}
} else {
$this->logline(' -- Row not found.');
$request = null;
}
return $request;
}
function select_all($fields, $tables, $conditions = null, $order = null, $allow_deleted = false, $limit = null, $page = 1, $group = null) {
$query = $this->select_query($fields, $tables, $conditions, $order, $group, $limit, $page, $allow_deleted);
$results = $this->query($query);
if (!$results) return null;
$returned = mysql_num_rows($results);
$s = ($returned == 1)? '': 's';
$this->logline(" -- Returned $returned row$s.");
$request = array();
if (is_string($fields)) $fetch_field = $fields;
$fieldname = is_string($fields)? ($dot = strrpos($fields, '.'))? substr($fields, $dot + 1): $fields: null;
while ($row = mysql_fetch_assoc($results)) {
$request[] = array_key_exists($fieldname, $row)? $row[$fieldname]: $row;
}
return $request;
}
function get($fields, $tables, $id) {
return $this->select($fields, $tables, array($this->primary_table($tables).'.id' => intval($id)));
}
function insert($table, $fields) {
//returns id of the new record, null if more than 1 row was inserted/modified.
$result = null;
$query = $this->insert_query($table, $fields);
$results = $this->query($query);
$affected = mysql_affected_rows($this->db);
if ($affected == 1) {
$result = mysql_insert_id($this->db);
$this->logline(' -- Inserted as #'.$result);
} else if ($affected > 1) {
$this->warn("Inserted $affected rows.");
} else {
$this->warn('Insert failed.');
}
return $result;
}
function update($table, $updates, $conditions) {
//returns the number of affected rows
if (!isset($conditions)) die('Empty $conditions in update!');
$this->query($this->update_query($table, $updates, $conditions));
$affected = mysql_affected_rows($this->db);
$s = ($affected == 1)? '': 's';
$this->logline(" -- Updated $affected row$s.");
return $affected;
}
function delete($table, $conditions) {
if ($this->common_fields) {
// Doesn't actually remove the rows from the database; sets the date_deleted field instead.
$deleted = $this->update($table, array('date_deleted' => $this->datetime()), $conditions);
} else {
$deleted = $this->purge($table, $conditions);
}
//returns number of affected rows
return $deleted;
}
function purge($table, $conditions) {
// Actually removes the rows from the database; use delete() unless you're certain this is what you want.
// returns: the number of affected (removed) rows
$this->query($this->delete_query($table, $conditions));
$affected = mysql_affected_rows($this->db);
$s = ($affected == 1)? '': 's';
$this->logline(" -- Deleted $affected row$s.");
return $affected;
}
function save($table, $updates, $conditions) {
// Update the record in the database, adding a new one if necessary
// returns: the id of the new or old row
if (!isset($conditions)) die('Empty $conditions in save!');
$old_vals = $this->select(is_array($updates)? array_keys($updates): $updates, $table, $conditions);
if (is_null($old_vals)) {
$this->logline(" -- Inserting new row.");
if (is_array($conditions)) $updates = is_array($updates)? array_merge($updates, $conditions): $conditions;
return $this->insert($table, $updates);
} else if (empty($updates) or ($old_vals == $updates)) {
$this->logline(" -- Nothing needs to be updated.");
$result = true;
} else {
$this->logline(" -- Updating old rows.");
$result = $this->update($table, $updates, $conditions);
}
if ($result and $this->common_fields) {
if (is_array($conditions) and array_key_exists('id', $conditions)) {
$result = $conditions['id'];
} else if (array_key_exists('id', $updates)) {
$result = $updates['id'];
} else if (array_key_exists('id', $old_vals)) {
$result = $old_vals['id'];
} else {
$result = $this->select('id', $table, $conditions);
}
}
return $result;
}
function get_max($table, $field, $conditions = null, $allow_deleted = false) {
if (!$allow_deleted) $conditions = $this->check_deleted($this->primary_table($table), $conditions);
$result = $this->query('SELECT MAX('.$field.') AS `maximum_value` FROM '.$table.' '.$this->conditions($conditions));
$value = 0;
if (!empty($result)) {
$row = mysql_fetch_assoc($result);
if (array_key_exists('maximum_value', $row)) {
$value = $row['maximum_value'];
}
}
return $value;
}
function get_property($table, $list_name, $item_id) {
$result = $this->select('property_options.value', array(
$table,
'property_options' => array('property_options.id' => $table.'.property_id'),
'property_lists' => array('property_lists.id' => 'property_options.list_id'),
), array(
$table.'.item_id' => $item_id,
'property_lists.list_name' => $list_name,
));
if (is_null($result)) {
$result = $this->select('property_options.value', array(
'property_options',
'property_lists' => array('property_lists.id' => 'property_options.list_id'),
), array(
'property_options.is_default' => true,
'property_lists.list_name' => $list_name,
));
}
return $result;
}
function set_property($table, $list_name, $value, $item_id) {
// Yes, this is messy. Can it be cleaned up?
$list = $this->select(array('id', 'allow_multiple', 'allow_custom'), 'property_lists',
array('list_name' => $list_name));
if (empty($list)) {
$this->warn('Missing property list: '.$list_name);
return false;
}
$options = $this->select_all(array($table.'.id', 'property_options.value'), array(
$table,
'property_options' => array('property_options.id' => $table.'.property_id'),
'property_lists' => array('property_lists.id' => 'property_options.list_id'),
), array(
$table.'.item_id' => $item_id,
'property_lists.list_name' => $list_name,
));
$option_ids = array();
foreach ($options as $option) {
if ($option['value'] == strval($value)) {
// Option already set.
$this->logline("-- Option '$value' already set for property list '$list_name'.");
return true;
} else {
$option_ids[] = $option['id'];
}
}
$option_id = $this->select('property_options.id', array(
'property_options',
'property_lists' => array('property_lists.id' => 'property_options.list_id'),
), array('property_lists.list_name' => $list_name, 'property_options.value' => $value));
if (empty($option_id)) {
if ($list['allow_custom']) {
$option_id = $this->insert('property_options', array('list_id' => $list['id'], 'value' => strval($value)), true);
if (empty($option_id)) return false;
} else {
$this->warn("Missing option '$value' in property list '$list_name'.");
return false;
}
}
if ($list['allow_multiple']) {
// Add a new row to the group
$result = $this->insert($table, array('item_id' => $item_id, 'property_id' => $option_id));
} else if (count($option_ids) == 1) {
// Update the old row directly
$result = $this->update($table, array('property_id' => $option_id), array('item_id' => $item_id));
} else {
// Delete any old rows, then add a new one
if ($option_ids) $this->delete($table, array('property_id' => $option_ids));
$result = $this->insert($table, array('item_id' => $item_id, 'property_id' => $option_id));
}
return $result;
}
function set_description($table, $row, $description) {
$desc_id = $this->get('description_id', $table, $row);
if ($desc_id and empty($description)) {
$result = $this->update('descriptions', array('value' => ''), array('id' => $desc_id));
} else if (empty($description)) {
$result = true;
} else if (is_null($desc_id)) {
$desc_id = $this->insert('descriptions', array('value' => $description), true);
if (empty($desc_id)) return false;
$result = $this->update($table, array('description_id' => $desc_id), array('id' => $row));
} else {
$result = $this->save('descriptions', array('value' => $description), array('id' => $desc_id));
}
return $result;
}
function has_column($table, $column) {
$result = false;
$description = $this->query('DESCRIBE ' . $this->escape($table));
if (!$results) return null;
while ($table_row = mysql_fetch_assoc($description)) {
if ($table_row['Field'] == $column) {
$result = true;
break;
}
}
return $result;
}
}
$db = new Database($db_info);
//$db->echo = true;
check_db_version();
//rollback_db(29);
?>