Commit feaeeffc authored by Carsten  Rose's avatar Carsten Rose
Browse files

recordLocking

DatabaseUpdateData.php: Table Form: recordLockTimeoutSeconds - default changed from 0 to 900 secs. Existing forms get the default timeout timout interval.
Dirty.php, QuickFormQuery.php: recordLockTimeoutSeconds will be retrieved from the Form.recordLockTimeoutSeconds definition (instead of parsing config.qfq.ini). Therefore a definition per form ist possible.
parent 4475fab6
...@@ -268,10 +268,9 @@ class QuickFormQuery { ...@@ -268,10 +268,9 @@ class QuickFormQuery {
// Check and release dirtyRecord. // Check and release dirtyRecord.
if ($formMode === FORM_DELETE || $formMode === FORM_SAVE) { if ($formMode === FORM_DELETE || $formMode === FORM_SAVE) {
$lockTimeout = $this->store->getVar(STORE_SYSTEM, SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS); $dirty = new Dirty();
$dirty = new Dirty($lockTimeout); $dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS],
$dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $recordId);
$recordId);
} }
if ($formMode === FORM_DELETE) { if ($formMode === FORM_DELETE) {
......
...@@ -55,7 +55,7 @@ $UPDATE_ARRAY = array( ...@@ -55,7 +55,7 @@ $UPDATE_ARRAY = array(
'0.19.0' => [ '0.19.0' => [
"ALTER TABLE `Form` ADD `dirtyMode` ENUM( 'exclusive', 'advisory', 'none' ) NOT NULL DEFAULT 'exclusive' AFTER `requiredParameter`", "ALTER TABLE `Form` ADD `dirtyMode` ENUM( 'exclusive', 'advisory', 'none' ) NOT NULL DEFAULT 'exclusive' AFTER `requiredParameter`",
"ALTER TABLE `Form` ADD `recordLockTimeoutSeconds` INT NOT NULL DEFAULT '0' AFTER `parameter`" "ALTER TABLE `Form` ADD `recordLockTimeoutSeconds` INT NOT NULL DEFAULT '900' AFTER `parameter`"
], ],
); );
......
...@@ -40,26 +40,14 @@ class Dirty { ...@@ -40,26 +40,14 @@ class Dirty {
*/ */
private $session = null; private $session = null;
/**
* @var array()
*/
private $lockTimeout = false;
/** /**
* Init class * Init class
*/ */
public function __construct($lockTimeout = false, $phpUnit = false) { public function __construct($phpUnit = false) {
$this->session = Session::getInstance($phpUnit); $this->session = Session::getInstance($phpUnit);
$this->client = Client::getParam(); $this->client = Client::getParam();
$this->db = new Database(); $this->db = new Database();
if($lockTimeout === false) {
$cfg = new Config();
$config = $cfg->readConfig('');
$this->lockTimeout = $config[SYSTEM_RECORD_LOCK_TIMEOUT_SECONDS];
} else {
$this->lockTimeout = $lockTimeout;
}
} }
/** /**
...@@ -83,7 +71,7 @@ class Dirty { ...@@ -83,7 +71,7 @@ class Dirty {
$recordId = empty($sipVars[SIP_RECORD_ID]) ? 0 : $sipVars[SIP_RECORD_ID]; $recordId = empty($sipVars[SIP_RECORD_ID]) ? 0 : $sipVars[SIP_RECORD_ID];
if($recordId>0) { if($recordId>0) {
$tableVars = $this->db->sql("SELECT tableName, dirtyMode FROM Form AS f WHERE f.name=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'"); $tableVars = $this->db->sql("SELECT tableName, dirtyMode, recordLockTimeoutSeconds FROM Form AS f WHERE f.name=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'");
} }
switch($this->client[API_LOCK_ACTION]) { switch($this->client[API_LOCK_ACTION]) {
...@@ -91,7 +79,7 @@ class Dirty { ...@@ -91,7 +79,7 @@ class Dirty {
$answer = $this->acquireDirty($recordId, $tableVars); $answer = $this->acquireDirty($recordId, $tableVars);
break; break;
case API_LOCK_ACTION_RELEASE: case API_LOCK_ACTION_RELEASE:
$answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $recordId); $answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS], $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $recordId);
break; break;
default; default;
throw new CodeException("Unknown action: " . $this->client[API_LOCK_ACTION], ERROR_DIRTY_UNKNOWN_ACTION); throw new CodeException("Unknown action: " . $this->client[API_LOCK_ACTION], ERROR_DIRTY_UNKNOWN_ACTION);
...@@ -127,8 +115,9 @@ class Dirty { ...@@ -127,8 +115,9 @@ class Dirty {
$recordDirty = $this->getRecordDirty($tableName, $recordId); $recordDirty = $this->getRecordDirty($tableName, $recordId);
// Check if the record is timed out // Check if the record is timed out
if (count($recordDirty) != 0 && $this->lockTimeout > 0) { if (count($recordDirty) != 0 && $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] > 0) {
$datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string($this->lockTimeout . " second")); $datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]),
date_interval_create_from_date_string($tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] . " second"));
if ($datetimeExpire <= date_create("now")) { if ($datetimeExpire <= date_create("now")) {
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]); $this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
$recordDirty=array(); $recordDirty=array();
...@@ -137,7 +126,7 @@ class Dirty { ...@@ -137,7 +126,7 @@ class Dirty {
if (count($recordDirty) == 0) { if (count($recordDirty) == 0) {
// No dirty record found. // No dirty record found.
$answer = $this->writeDirty($this->client[SIP_SIP], $recordId, $tableName, $formDirtyMode, $feUser); $answer = $this->writeDirty($this->client[SIP_SIP], $recordId, $tableVars, $feUser);
} else { } else {
$answer = $this->conflict($recordDirty, $formDirtyMode); $answer = $this->conflict($recordDirty, $formDirtyMode);
} }
...@@ -204,14 +193,16 @@ class Dirty { ...@@ -204,14 +193,16 @@ class Dirty {
* *
* @param string $s SIP given by URL GET * @param string $s SIP given by URL GET
* @param int $recordId extracted from SIP * @param int $recordId extracted from SIP
* @param string $tableName extracted from SIP * @param array $tableVars
* @param string $formDirtyMode
* @param string $feUser * @param string $feUser
* @return array * @return array
* @throws CodeException * @throws CodeException
* @throws DbException * @throws DbException
*/ */
private function writeDirty($s, $recordId, $tableName, $formDirtyMode, $feUser) { private function writeDirty($s, $recordId,array $tableVars, $feUser) {
$tableName = $tableVars[F_TABLE_NAME];
$formDirtyMode = $tableVars[F_DIRTY_MODE];
$record = $this->db->sql("SELECT * FROM $tableName WHERE id=?", ROW_EXPECT_1, [$recordId], "Record to tag 'dirty' not found."); $record = $this->db->sql("SELECT * FROM $tableName WHERE id=?", ROW_EXPECT_1, [$recordId], "Record to tag 'dirty' not found.");
...@@ -220,9 +211,12 @@ class Dirty { ...@@ -220,9 +211,12 @@ class Dirty {
// Write 'dirty' record // Write 'dirty' record
$this->db->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `recordModified`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " . $this->db->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `recordModified`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,? )", ROW_REGULAR, [$s, $tableName, $recordId, $recordModified, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode, $this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]); "VALUES ( ?,?,?,?,?,?,?,?,? )", ROW_REGULAR,
[$s, $tableName, $recordId, $recordModified, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode,
$this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]);
return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => $this->lockTimeout ]; return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '',
API_LOCK_TIMEOUT => $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] ];
} }
...@@ -230,15 +224,16 @@ class Dirty { ...@@ -230,15 +224,16 @@ class Dirty {
* Release a dirtyRecord. This is only possible if the current user owns the dirtyRecord. * Release a dirtyRecord. This is only possible if the current user owns the dirtyRecord.
* In case of not owner, throws an exception and the save should break. * In case of not owner, throws an exception and the save should break.
* *
* @param string $formMode FORM_DELETE, FORM_SAVE * @param string $formMode FORM_DELETE, FORM_SAVE
* @param string $dirtyMode DIRTY_MODE_EXCLUSIVE, DIRTY_MODE_ADVISORY, DIRTY_MODE_NONE * @param int $lockTimeout
* @param string $dirtyMode DIRTY_MODE_EXCLUSIVE, DIRTY_MODE_ADVISORY, DIRTY_MODE_NONE
* @param string $tableName * @param string $tableName
* @param int $recordId * @param int $recordId
* @return array * @return array
* @throws CodeException * @throws CodeException
* @throws UserFormException * @throws UserFormException
*/ */
public function checkDirtyAndRelease($formMode, $dirtyMode, $tableName, $recordId) { public function checkDirtyAndRelease($formMode, $lockTimeout, $dirtyMode, $tableName, $recordId) {
$answer = [ API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '']; $answer = [ API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
...@@ -286,8 +281,8 @@ class Dirty { ...@@ -286,8 +281,8 @@ class Dirty {
} }
// Check if the record is timed out // Check if the record is timed out
if ($this->lockTimeout > 0) { if ($lockTimeout > 0) {
$datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string($this->lockTimeout . " second")); $datetimeExpire = date_add(date_create($recordDirty[COLUMN_MODIFIED]), date_interval_create_from_date_string($lockTimeout . " second"));
if ($datetimeExpire <= date_create("now")) { if ($datetimeExpire <= date_create("now")) {
$this->deleteDirtyRecord($recordDirty[COLUMN_ID]); $this->deleteDirtyRecord($recordDirty[COLUMN_ID]);
return $answer; return $answer;
......
...@@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS `Form` ( ...@@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS `Form` (
`escapeTypeDefault` VARCHAR(32) NOT NULL DEFAULT 'c', `escapeTypeDefault` VARCHAR(32) NOT NULL DEFAULT 'c',
`render` ENUM('bootstrap', 'table', 'plain') NOT NULL DEFAULT 'bootstrap', `render` ENUM('bootstrap', 'table', 'plain') NOT NULL DEFAULT 'bootstrap',
`requiredParameter` VARCHAR(255) NOT NULL DEFAULT '', `requiredParameter` VARCHAR(255) NOT NULL DEFAULT '',
`dirtyMode` ENUM('exclusive', 'advisory', 'none') NOT NULL DEFAULT 'exclusive', `dirtyMode` ENUM('exclusive', 'advisory', 'none') NOT NULL DEFAULT 'exclusive',
`showButton` SET('new', 'delete', 'close', 'save') NOT NULL DEFAULT 'new,delete,close,save', `showButton` SET('new', 'delete', 'close', 'save') NOT NULL DEFAULT 'new,delete,close,save',
`multiMode` ENUM('none', 'horizontal', 'vertical') NOT NULL DEFAULT 'none', `multiMode` ENUM('none', 'horizontal', 'vertical') NOT NULL DEFAULT 'none',
`multiSql` TEXT NOT NULL, `multiSql` TEXT NOT NULL,
...@@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS `Form` ( ...@@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS `Form` (
`bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '', `bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '',
`parameter` TEXT NOT NULL, `parameter` TEXT NOT NULL,
`recordLockTimeoutSeconds` INT(11) NOT NULL DEFAULT 0, `recordLockTimeoutSeconds` INT(11) NOT NULL DEFAULT 900,
`deleted` ENUM('yes', 'no') NOT NULL DEFAULT 'no', `deleted` ENUM('yes', 'no') NOT NULL DEFAULT 'no',
`modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
...@@ -58,54 +58,54 @@ CREATE TABLE IF NOT EXISTS `Form` ( ...@@ -58,54 +58,54 @@ CREATE TABLE IF NOT EXISTS `Form` (
#DROP TABLE IF EXISTS `FormElement`; #DROP TABLE IF EXISTS `FormElement`;
CREATE TABLE IF NOT EXISTS `FormElement` ( CREATE TABLE IF NOT EXISTS `FormElement` (
`id` INT(11) NOT NULL AUTO_INCREMENT, `id` INT(11) NOT NULL AUTO_INCREMENT,
`formId` INT(11) NOT NULL, `formId` INT(11) NOT NULL,
`feIdContainer` INT(11) NOT NULL DEFAULT '0', `feIdContainer` INT(11) NOT NULL DEFAULT '0',
`dynamicUpdate` ENUM('yes', 'no') NOT NULL DEFAULT 'no', `dynamicUpdate` ENUM('yes', 'no') NOT NULL DEFAULT 'no',
`enabled` ENUM('yes', 'no') NOT NULL DEFAULT 'yes', `enabled` ENUM('yes', 'no') NOT NULL DEFAULT 'yes',
`name` VARCHAR(255) NOT NULL DEFAULT '', `name` VARCHAR(255) NOT NULL DEFAULT '',
`label` VARCHAR(255) NOT NULL DEFAULT '', `label` VARCHAR(255) NOT NULL DEFAULT '',
`mode` ENUM('show', 'required', 'readonly', 'hidden') NOT NULL DEFAULT 'show', `mode` ENUM('show', 'required', 'readonly', 'hidden') NOT NULL DEFAULT 'show',
`modeSql` TEXT NOT NULL, `modeSql` TEXT NOT NULL,
`class` ENUM('native', 'action', 'container') NOT NULL DEFAULT 'native', `class` ENUM('native', 'action', 'container') NOT NULL DEFAULT 'native',
`type` ENUM('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra', 'gridJQW', 'text', `type` ENUM('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra', 'gridJQW', 'text',
'editor', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload', 'editor', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload',
'fieldset', 'pill', 'templateGroup', 'fieldset', 'pill', 'templateGroup',
'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad',
'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text', 'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text',
`subrecordOption` SET('edit', 'delete', 'new') NOT NULL DEFAULT '', `subrecordOption` SET('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM('none', 'specialchar') NOT NULL DEFAULT 'specialchar', `encode` ENUM('none', 'specialchar') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM('alnumx', 'digit', 'numerical', 'email', 'min|max', 'min|max date', 'pattern', 'allbut', 'all') NOT NULL DEFAULT 'alnumx', `checkType` ENUM('alnumx', 'digit', 'numerical', 'email', 'min|max', 'min|max date', 'pattern', 'allbut', 'all') NOT NULL DEFAULT 'alnumx',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '', `checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
`onChange` VARCHAR(255) NOT NULL DEFAULT '', `onChange` VARCHAR(255) NOT NULL DEFAULT '',
`ord` INT(11) NOT NULL DEFAULT '0', `ord` INT(11) NOT NULL DEFAULT '0',
`tabindex` INT(11) NOT NULL DEFAULT '0', `tabindex` INT(11) NOT NULL DEFAULT '0',
`size` VARCHAR(255) NOT NULL DEFAULT '', `size` VARCHAR(255) NOT NULL DEFAULT '',
`maxLength` VARCHAR(255) NOT NULL DEFAULT '', `maxLength` VARCHAR(255) NOT NULL DEFAULT '',
`bsLabelColumns` VARCHAR(255) NOT NULL DEFAULT '', `bsLabelColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsInputColumns` VARCHAR(255) NOT NULL DEFAULT '', `bsInputColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '', `bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '',
`rowLabelInputNote` SET('row', 'label', '/label', 'input', '/input', 'note', '/note', '/row') NOT NULL DEFAULT 'row,label,/label,input,/input,note,/note,/row', `rowLabelInputNote` SET('row', 'label', '/label', 'input', '/input', 'note', '/note', '/row') NOT NULL DEFAULT 'row,label,/label,input,/input,note,/note,/row',
`note` TEXT NOT NULL, `note` TEXT NOT NULL,
`adminNote` TEXT NOT NULL, `adminNote` TEXT NOT NULL,
`tooltip` VARCHAR(255) NOT NULL DEFAULT '', `tooltip` VARCHAR(255) NOT NULL DEFAULT '',
`placeholder` VARCHAR(2048) NOT NULL DEFAULT '', `placeholder` VARCHAR(2048) NOT NULL DEFAULT '',
`value` TEXT NOT NULL, `value` TEXT NOT NULL,
`sql1` TEXT NOT NULL, `sql1` TEXT NOT NULL,
`parameter` TEXT NOT NULL, `parameter` TEXT NOT NULL,
`clientJs` TEXT NOT NULL, `clientJs` TEXT NOT NULL,
`feGroup` VARCHAR(255) NOT NULL DEFAULT '', `feGroup` VARCHAR(255) NOT NULL DEFAULT '',
`deleted` ENUM('yes', 'no') NOT NULL DEFAULT 'no', `deleted` ENUM('yes', 'no') NOT NULL DEFAULT 'no',
`modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `formId` (`formId`), KEY `formId` (`formId`),
...@@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS `Dirty` ( ...@@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS `Dirty` (
`sip` VARCHAR(255) NOT NULL, `sip` VARCHAR(255) NOT NULL,
`tableName` VARCHAR(255) NOT NULL, `tableName` VARCHAR(255) NOT NULL,
`recordId` INT(11) NOT NULL, `recordId` INT(11) NOT NULL,
`expire` DATETIME NOT NULL, `expire` DATETIME NOT NULL,
`recordModified` DATETIME NOT NULL, `recordModified` DATETIME NOT NULL,
`feUser` VARCHAR(255) NOT NULL, `feUser` VARCHAR(255) NOT NULL,
`qfqUserSessionCookie` VARCHAR(255) NOT NULL, `qfqUserSessionCookie` VARCHAR(255) NOT NULL,
...@@ -214,7 +214,8 @@ VALUES ...@@ -214,7 +214,8 @@ VALUES
(1, 'modified', 'Modified', 'readonly', 'text', 'all', 'native', 370, 0, 20, '', '', '', '', '', 3, '', '', '', 'specialchar', 'no', ''), (1, 'modified', 'Modified', 'readonly', 'text', 'all', 'native', 370, 0, 20, '', '', '', '', '', 3, '', '', '', 'specialchar', 'no', ''),
(1, 'created', 'Created', 'readonly', 'text', 'all', 'native', 380, 0, 20, '', '', '', '', '', 3, '', '', '', 'specialchar', 'no', ''), (1, 'created', 'Created', 'readonly', 'text', 'all', 'native', 380, 0, 20, '', '', '', '', '', 3, '', '', '', 'specialchar', 'no', ''),
(1, 'multi', 'Multi', 'show', 'fieldset', 'all', 'native', 400, 0, 0, '', '', '', '', '', 4, '', '', '', 'specialchar', 'no', ''), (1, 'multi', 'Multi', 'show', 'fieldset', 'all', 'native', 400, 0, 0, '', '', '', '', '', 4, '', '', '',
'specialchar', 'no', ''),
(1, 'multiMode', 'Multi Mode', 'show', 'radio', 'all', 'native', 410, 0, 0, '', '', '', '', '', 4, '', '', '', (1, 'multiMode', 'Multi Mode', 'show', 'radio', 'all', 'native', 410, 0, 0, '', '', '', '', '', 4, '', '', '',
'specialchar', 'no', ''), 'specialchar', 'no', ''),
(1, 'multiSql', 'Multi SQL', 'show', 'text', 'all', 'native', 420, '40,3', 0, '', '', '', '', '', 4, '', '', '', (1, 'multiSql', 'Multi SQL', 'show', 'text', 'all', 'native', 420, '40,3', 0, '', '', '', '', '', 4, '', '', '',
...@@ -322,10 +323,10 @@ CREATE TABLE IF NOT EXISTS `MailLog` ( ...@@ -322,10 +323,10 @@ CREATE TABLE IF NOT EXISTS `MailLog` (
`id` INT(11) NOT NULL AUTO_INCREMENT, `id` INT(11) NOT NULL AUTO_INCREMENT,
`grId` INT(11) NOT NULL DEFAULT '0', `grId` INT(11) NOT NULL DEFAULT '0',
`xId` INT(11) NOT NULL DEFAULT '0', `xId` INT(11) NOT NULL DEFAULT '0',
`receiver` TEXT NOT NULL, `receiver` TEXT NOT NULL,
`sender` VARCHAR(255) NOT NULL DEFAULT '', `sender` VARCHAR(255) NOT NULL DEFAULT '',
`subject` VARCHAR(255) NOT NULL DEFAULT '', `subject` VARCHAR(255) NOT NULL DEFAULT '',
`body` TEXT NOT NULL, `body` TEXT NOT NULL,
`header` VARCHAR(255) NOT NULL DEFAULT '', `header` VARCHAR(255) NOT NULL DEFAULT '',
`attach` VARCHAR(255) NOT NULL DEFAULT '', `attach` VARCHAR(255) NOT NULL DEFAULT '',
`src` VARCHAR(255) NOT NULL DEFAULT '', `src` VARCHAR(255) NOT NULL DEFAULT '',
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment