Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const cICL = Ci.calIChangeLog;
/**
* CalStorageStatements contains the mozIStorageBaseStatements used by the
* various storage calendar models. Remember to call the finalize() method when
* shutting down the db.
*/
export class CalStorageStatements {
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectEvent = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectTodo = null;
/**
* @type {mozIStorageAsyncStatement} mSelectNonRecurringEventsByRange
*/
mSelectNonRecurringEventsByRange = null;
/**
* @type {mozIStorageAsyncStatement} mSelectNonRecurringTodosByRange
*/
mSelectNonRecurringTodosByRange = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAttendeesForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAttendeesForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllAttendees = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectPropertiesForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectPropertiesForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllProperties = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectParametersForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectParametersForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllParameters = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectRecurrenceForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllRecurrences = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectEventsWithRecurrence = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectTodosWithRecurrence = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectEventExceptions = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllEventExceptions = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectTodoExceptions = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllTodoExceptions = null;
/**
* @type {mozIStorageStatement}
*/
mSelectMetaData = null;
/**
* @type {mozIStorageStatement}
*/
mSelectAllMetaData = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectRelationsForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllRelations = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectRelationsForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAlarmsForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAlarmsForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllAlarms = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAttachmentsForItem = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAttachmentsForItemWithRecurrenceId = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mSelectAllAttachments = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertEvent = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertTodo = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertProperty = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertParameter = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertAttendee = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertRecurrence = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertAttachment = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertRelation = null;
/**
* @type {mozIStorageStatement}
*/
mInsertMetaData = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mInsertAlarm = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mEditEventOfflineFlag = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mEditTodoOfflineFlag = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteEvent = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteTodo = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteAttendees = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteProperties = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteParameters = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteRecurrence = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteAttachments = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteRelations = null;
/**
* @type {mozIStorageStatement}
*/
mDeleteMetaData = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteAlarms = null;
/**
* @type {mozIStorageAsyncStatement[]}
*/
mDeleteEventExtras = [];
/**
* @type {mozIStorageAsyncStatement[]}
*/
mDeleteTodoExtras = [];
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteAllEvents = null;
/**
* @type {mozIStorageAsyncStatement}
*/
mDeleteAllTodos = null;
/**
* @type {mozIStorageStatement}
*/
mDeleteAllMetaData = null;
/**
* @param {CalStorageDatabase} db
*
* @throws - If unable to initialize SQL statements.
*/
constructor(db) {
this.mSelectEvent = db.createAsyncStatement(
`SELECT * FROM cal_events
WHERE id = :id
AND cal_id = :cal_id
AND recurrence_id IS NULL
LIMIT 1`
);
this.mSelectTodo = db.createAsyncStatement(
`SELECT * FROM cal_todos
WHERE id = :id
AND cal_id = :cal_id
AND recurrence_id IS NULL
LIMIT 1`
);
// The more readable version of the next where-clause is:
// WHERE ((event_end > :range_start OR
// (event_end = :range_start AND
// event_start = :range_start))
// AND event_start < :range_end)
//
// but that doesn't work with floating start or end times. The logic
// is the same though.
// For readability, a few helpers:
const floatingEventStart = "event_start_tz = 'floating' AND event_start";
const nonFloatingEventStart = "event_start_tz != 'floating' AND event_start";
const floatingEventEnd = "event_end_tz = 'floating' AND event_end";
const nonFloatingEventEnd = "event_end_tz != 'floating' AND event_end";
// The query needs to take both floating and non floating into account.
this.mSelectNonRecurringEventsByRange = db.createAsyncStatement(
`SELECT * FROM cal_events
WHERE
((${floatingEventEnd} > :range_start + :start_offset) OR
(${nonFloatingEventEnd} > :range_start) OR
(((${floatingEventEnd} = :range_start + :start_offset) OR
(${nonFloatingEventEnd} = :range_start)) AND
((${floatingEventStart} = :range_start + :start_offset) OR
(${nonFloatingEventStart} = :range_start))))
AND
((${floatingEventStart} < :range_end + :end_offset) OR
(${nonFloatingEventStart} < :range_end))
AND cal_id = :cal_id AND flags & 16 == 0 AND recurrence_id IS NULL
AND ((:offline_journal IS NULL
AND (offline_journal IS NULL
OR offline_journal != ${cICL.OFFLINE_FLAG_DELETED_RECORD}))
OR (offline_journal == :offline_journal))`
);
//
// WHERE (due > rangeStart AND (entry IS NULL OR entry < rangeEnd)) OR
// (due = rangeStart AND (entry IS NULL OR entry = rangeStart)) OR
// (due IS NULL AND (entry >= rangeStart AND entry < rangeEnd)) OR
// (entry IS NULL AND (completed > rangeStart OR completed IS NULL))
//
const floatingTodoEntry = "todo_entry_tz = 'floating' AND todo_entry";
const nonFloatingTodoEntry = "todo_entry_tz != 'floating' AND todo_entry";
const floatingTodoDue = "todo_due_tz = 'floating' AND todo_due";
const nonFloatingTodoDue = "todo_due_tz != 'floating' AND todo_due";
const floatingCompleted = "todo_completed_tz = 'floating' AND todo_completed";
const nonFloatingCompleted = "todo_completed_tz != 'floating' AND todo_completed";
this.mSelectNonRecurringTodosByRange = db.createAsyncStatement(
`SELECT * FROM cal_todos
WHERE
((((${floatingTodoDue} > :range_start + :start_offset) OR
(${nonFloatingTodoDue} > :range_start)) AND
((todo_entry IS NULL) OR
((${floatingTodoEntry} < :range_end + :end_offset) OR
(${nonFloatingTodoEntry} < :range_end)))) OR
(((${floatingTodoDue} = :range_start + :start_offset) OR
(${nonFloatingTodoDue} = :range_start)) AND
((todo_entry IS NULL) OR
((${floatingTodoEntry} = :range_start + :start_offset) OR
(${nonFloatingTodoEntry} = :range_start)))) OR
((todo_due IS NULL) AND
(((${floatingTodoEntry} >= :range_start + :start_offset) OR
(${nonFloatingTodoEntry} >= :range_start)) AND
((${floatingTodoEntry} < :range_end + :end_offset) OR
(${nonFloatingTodoEntry} < :range_end)))) OR
((todo_entry IS NULL) AND
(((${floatingCompleted} > :range_start + :start_offset) OR
(${nonFloatingCompleted} > :range_start)) OR
(todo_completed IS NULL))))
AND cal_id = :cal_id AND flags & 16 == 0 AND recurrence_id IS NULL
AND ((:offline_journal IS NULL
AND (offline_journal IS NULL
OR offline_journal != ${cICL.OFFLINE_FLAG_DELETED_RECORD}))
OR (offline_journal == :offline_journal))`
);
this.mSelectEventsWithRecurrence = db.createAsyncStatement(
`SELECT * FROM cal_events
WHERE flags & 16 == 16
AND cal_id = :cal_id
AND recurrence_id is NULL`
);
this.mSelectTodosWithRecurrence = db.createAsyncStatement(
`SELECT * FROM cal_todos
WHERE flags & 16 == 16
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectEventExceptions = db.createAsyncStatement(
`SELECT * FROM cal_events
WHERE id = :id
AND cal_id = :cal_id
AND recurrence_id IS NOT NULL`
);
this.mSelectAllEventExceptions = db.createAsyncStatement(
`SELECT * FROM cal_events
WHERE cal_id = :cal_id
AND recurrence_id IS NOT NULL`
);
this.mSelectTodoExceptions = db.createAsyncStatement(
`SELECT * FROM cal_todos
WHERE id = :id
AND cal_id = :cal_id
AND recurrence_id IS NOT NULL`
);
this.mSelectAllTodoExceptions = db.createAsyncStatement(
`SELECT * FROM cal_todos
WHERE cal_id = :cal_id
AND recurrence_id IS NOT NULL`
);
this.mSelectAttendeesForItem = db.createAsyncStatement(
`SELECT * FROM cal_attendees
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectAttendeesForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT * FROM cal_attendees
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllAttendees = db.createAsyncStatement(
`SELECT item_id, icalString FROM cal_attendees
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectPropertiesForItem = db.createAsyncStatement(
`SELECT * FROM cal_properties
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectPropertiesForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT * FROM cal_properties
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllProperties = db.createAsyncStatement(
`SELECT item_id, key, value FROM cal_properties
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectParametersForItem = db.createAsyncStatement(
`SELECT * FROM cal_parameters
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectParametersForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT * FROM cal_parameters
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllParameters = db.createAsyncStatement(
`SELECT item_id, key1, key2, value FROM cal_parameters
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectRecurrenceForItem = db.createAsyncStatement(
`SELECT * FROM cal_recurrence
WHERE item_id = :item_id
AND cal_id = :cal_id`
);
this.mSelectAllRecurrences = db.createAsyncStatement(
`SELECT item_id, icalString FROM cal_recurrence
WHERE cal_id = :cal_id`
);
this.mSelectAttachmentsForItem = db.createAsyncStatement(
`SELECT * FROM cal_attachments
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectAttachmentsForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT * FROM cal_attachments
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllAttachments = db.createAsyncStatement(
`SELECT item_id, icalString FROM cal_attachments
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectRelationsForItem = db.createAsyncStatement(
`SELECT * FROM cal_relations
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectRelationsForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT * FROM cal_relations
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllRelations = db.createAsyncStatement(
`SELECT item_id, icalString FROM cal_relations
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectMetaData = db.createStatement(
`SELECT * FROM cal_metadata
WHERE item_id = :item_id
AND cal_id = :cal_id`
);
this.mSelectAllMetaData = db.createStatement(
`SELECT * FROM cal_metadata
WHERE cal_id = :cal_id`
);
this.mSelectAlarmsForItem = db.createAsyncStatement(
`SELECT icalString FROM cal_alarms
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id IS NULL`
);
this.mSelectAlarmsForItemWithRecurrenceId = db.createAsyncStatement(
`SELECT icalString FROM cal_alarms
WHERE item_id = :item_id
AND cal_id = :cal_id
AND recurrence_id = :recurrence_id
AND recurrence_id_tz = :recurrence_id_tz`
);
this.mSelectAllAlarms = db.createAsyncStatement(
`SELECT item_id, icalString FROM cal_alarms
WHERE cal_id = :cal_id
AND recurrence_id IS NULL`
);
// insert statements
this.mInsertEvent = db.createAsyncStatement(
`INSERT INTO cal_events
(cal_id, id, time_created, last_modified,
title, priority, privacy, ical_status, flags,
event_start, event_start_tz, event_end, event_end_tz, event_stamp,
recurrence_id, recurrence_id_tz, alarm_last_ack)
VALUES (:cal_id, :id, :time_created, :last_modified,
:title, :priority, :privacy, :ical_status, :flags,
:event_start, :event_start_tz, :event_end, :event_end_tz, :event_stamp,
:recurrence_id, :recurrence_id_tz, :alarm_last_ack)`
);
this.mInsertTodo = db.createAsyncStatement(
`INSERT INTO cal_todos
(cal_id, id, time_created, last_modified,
title, priority, privacy, ical_status, flags,
todo_entry, todo_entry_tz, todo_due, todo_due_tz, todo_stamp,
todo_completed, todo_completed_tz, todo_complete,
recurrence_id, recurrence_id_tz, alarm_last_ack)
VALUES (:cal_id, :id, :time_created, :last_modified,
:title, :priority, :privacy, :ical_status, :flags,
:todo_entry, :todo_entry_tz, :todo_due, :todo_due_tz, :todo_stamp,
:todo_completed, :todo_completed_tz, :todo_complete,
:recurrence_id, :recurrence_id_tz, :alarm_last_ack)`
);
this.mInsertProperty = db.createAsyncStatement(
`INSERT INTO cal_properties (cal_id, item_id, recurrence_id, recurrence_id_tz, key, value)
VALUES (:cal_id, :item_id, :recurrence_id, :recurrence_id_tz, :key, :value)`
);
this.mInsertParameter = db.createAsyncStatement(
`INSERT INTO cal_parameters (cal_id, item_id, recurrence_id, recurrence_id_tz, key1, key2, value)
VALUES (:cal_id, :item_id, :recurrence_id, :recurrence_id_tz, :key1, :key2, :value)`
);
this.mInsertAttendee = db.createAsyncStatement(
`INSERT INTO cal_attendees
(cal_id, item_id, recurrence_id, recurrence_id_tz, icalString)
VALUES (:cal_id, :item_id, :recurrence_id, :recurrence_id_tz, :icalString)`
);
this.mInsertRecurrence = db.createAsyncStatement(
`INSERT INTO cal_recurrence
(cal_id, item_id, icalString)
VALUES (:cal_id, :item_id, :icalString)`
);
this.mInsertAttachment = db.createAsyncStatement(
`INSERT INTO cal_attachments
(cal_id, item_id, icalString, recurrence_id, recurrence_id_tz)
VALUES (:cal_id, :item_id, :icalString, :recurrence_id, :recurrence_id_tz)`
);
this.mInsertRelation = db.createAsyncStatement(
`INSERT INTO cal_relations
(cal_id, item_id, icalString, recurrence_id, recurrence_id_tz)
VALUES (:cal_id, :item_id, :icalString, :recurrence_id, :recurrence_id_tz)`
);
this.mInsertMetaData = db.createStatement(
`INSERT INTO cal_metadata
(cal_id, item_id, value)
VALUES (:cal_id, :item_id, :value)`
);
this.mInsertAlarm = db.createAsyncStatement(
`INSERT INTO cal_alarms
(cal_id, item_id, icalString, recurrence_id, recurrence_id_tz)
VALUES (:cal_id, :item_id, :icalString, :recurrence_id, :recurrence_id_tz)`
);
// Offline Operations
this.mEditEventOfflineFlag = db.createStatement(
`UPDATE cal_events SET offline_journal = :offline_journal
WHERE id = :id
AND cal_id = :cal_id`
);
this.mEditTodoOfflineFlag = db.createStatement(
`UPDATE cal_todos SET offline_journal = :offline_journal
WHERE id = :id
AND cal_id = :cal_id`
);
// delete statements
this.mDeleteEvent = db.createAsyncStatement(
"DELETE FROM cal_events WHERE id = :id AND cal_id = :cal_id"
);
this.mDeleteTodo = db.createAsyncStatement(
"DELETE FROM cal_todos WHERE id = :id AND cal_id = :cal_id"
);
this.mDeleteAttendees = db.createAsyncStatement(
"DELETE FROM cal_attendees WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteProperties = db.createAsyncStatement(
"DELETE FROM cal_properties WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteParameters = db.createAsyncStatement(
"DELETE FROM cal_parameters WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteRecurrence = db.createAsyncStatement(
"DELETE FROM cal_recurrence WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteAttachments = db.createAsyncStatement(
"DELETE FROM cal_attachments WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteRelations = db.createAsyncStatement(
"DELETE FROM cal_relations WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteMetaData = db.createStatement(
"DELETE FROM cal_metadata WHERE item_id = :item_id AND cal_id = :cal_id"
);
this.mDeleteAlarms = db.createAsyncStatement(
"DELETE FROM cal_alarms WHERE item_id = :item_id AND cal_id = :cal_id"
);
// These are only used when deleting an entire calendar
const extrasTables = [
"cal_attendees",
"cal_properties",
"cal_parameters",
"cal_recurrence",
"cal_attachments",
"cal_metadata",
"cal_relations",
"cal_alarms",
];
this.mDeleteEventExtras = [];
this.mDeleteTodoExtras = [];
for (const table in extrasTables) {
this.mDeleteEventExtras[table] = db.createAsyncStatement(
`DELETE FROM ${extrasTables[table]}
WHERE item_id IN
(SELECT id FROM cal_events WHERE cal_id = :cal_id)
AND cal_id = :cal_id`
);
this.mDeleteTodoExtras[table] = db.createAsyncStatement(
`DELETE FROM ${extrasTables[table]}
WHERE item_id IN
(SELECT id FROM cal_todos WHERE cal_id = :cal_id)
AND cal_id = :cal_id`
);
}
// Note that you must delete the "extras" _first_ using the above two
// statements, before you delete the events themselves.
this.mDeleteAllEvents = db.createAsyncStatement(
"DELETE from cal_events WHERE cal_id = :cal_id"
);
this.mDeleteAllTodos = db.createAsyncStatement("DELETE from cal_todos WHERE cal_id = :cal_id");
this.mDeleteAllMetaData = db.createStatement("DELETE FROM cal_metadata WHERE cal_id = :cal_id");
}
/**
* Ensures all Db statements are properly cleaned up before shutdown by
* calling their finalize() method.
*/
finalize() {
for (const key of Object.keys(this)) {
if (this[key] instanceof Ci.mozIStorageBaseStatement) {
this[key].finalize();
}
}
for (const stmt of this.mDeleteEventExtras) {
stmt.finalize();
}
for (const stmt of this.mDeleteTodoExtras) {
stmt.finalize();
}
}
}