import { type QueryDefinitions, validatedDefinitions } from './querytypes'; import { boolean, discordSnowflake, jsonArray, nullable, string, substring, tableIdentifierOrId, tableIdentifierSubstring, timestamp, URL } from './validators'; import { extract, guaranteedSingleton, jsonParser, nothing, rows, singleton, writeCount } from './transformers'; export const DatabaseQueries = validatedDefinitions({ autocompleteTable: { query: `WITH matchingIds (id) AS (SELECT DISTINCT rollableTableIdentifiers.tableId AS id FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier LIKE ?1 ESCAPE '\\' UNION SELECT DISTINCT rollableTableHeaders.tableId AS id FROM rollableTableHeaders WHERE rollableTableHeaders.header LIKE ?1 ESCAPE '\\' UNION SELECT DISTINCT rollableTableBadges.id AS id FROM rollableTableBadges WHERE rollableTableBadges.badge LIKE ?1 ESCAPE '\\') SELECT rollableTables.identifier AS identifier, rollableTables.name AS name, rollableTables.emoji AS emoji FROM rollableTables WHERE ?1 = '%' OR rollableTables.id IN matchingIds LIMIT 25;`, parameters: { 'tableIdentifierSubstring': { validator: tableIdentifierSubstring, index: 1 } }, output: rows<{ identifier: string, name: string, emoji: string }>() }, autocompleteTextForDiscordSet: { query: `WITH matchingTables (id) AS (SELECT DISTINCT rollableTableIdentifiers.tableId AS id FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier LIKE ?2 ESCAPE '\\' UNION SELECT DISTINCT rollableTableHeaders.tableId AS id FROM rollableTableHeaders WHERE rollableTableHeaders.header LIKE ?2 ESCAPE '\\' UNION SELECT DISTINCT rollableTableBadges.id AS id FROM rollableTableBadges WHERE rollableTableBadges.badge LIKE ?2 ESCAPE '\\'), rollableSets (id) AS (SELECT resultSets.id FROM resultSets WHERE (?4 AND resultSets.global) OR resultSets.discordSnowflake = ?1) SELECT rollableResults.text AS text FROM rollableResults WHERE rollableResults.tableId IN matchingTables AND EXISTS(SELECT resultMappings.resultId FROM resultMappings WHERE resultMappings.setId IN rollableSets AND resultMappings.resultId = rollableResults.id) AND (?3 = '%' OR rollableResults.text LIKE ?3 ESCAPE '\\') ORDER BY (rollableResults.text LIKE SUBSTR(1, ?3) ESCAPE '\\') DESC, LENGTH(rollableResults.text) LIMIT 25;`, parameters: { 'setSnowflake': { validator: discordSnowflake, index: 1 }, 'tableIdentifierSubstring': { validator: tableIdentifierSubstring, index: 2 }, 'pattern': { validator: substring, index: 3 }, 'includeGlobal': { validator: boolean, index: 4 } }, output: rows<{ text: string }>() }, addResultForAddMapping: { query: `WITH rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE (rollableTableIdentifiers.identifier = ?1 OR rollableTableIdentifiers.tableId = ?1)) INSERT OR IGNORE INTO rollableResults (tableId, text) VALUES ((SELECT rollableTable.id FROM rollableTable), ?2);`, parameters: { 'tableIdentifier': { validator: tableIdentifierOrId, index: 1 }, 'text': { validator: string, index: 2 } }, output: nothing() }, addDiscordAuthorForAddMapping: { query: `WITH authorshipType (id) AS (SELECT authorshipTypes.id FROM authorshipTypes WHERE authorshipTypes.name = 'Discord contributor') INSERT INTO authors (name, url, discordSnowflake, discordUsername, authorshipTypeId) VALUES (NULL, NULL, ?1, ?2, (SELECT authorshipType.id FROM authorshipType)) ON CONFLICT DO UPDATE SET discordUsername = ?2;`, parameters: { 'userSnowflake': { validator: discordSnowflake, index: 1 }, 'username': { validator: string, index: 2 } }, output: nothing() }, addDiscordSetForAddMapping: { query: `INSERT OR IGNORE INTO resultSets (name, description, discordSnowflake, creatorId, global) VALUES (NULL, NULL, ?1, (SELECT authors.id FROM authors WHERE authors.discordSnowflake = ?2), FALSE)`, parameters: { 'setSnowflake': { validator: discordSnowflake, index: 1 }, 'userSnowflake': { validator: discordSnowflake, index: 2 } }, output: nothing() }, addDiscordResultMapping: { query: `WITH rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier = ?2 OR rollableTableIdentifiers.tableId = ?2 LIMIT 1), rollableResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.text = ?3 AND rollableResults.tableId = (SELECT id FROM rollableTable) LIMIT 1), resultSet (id) AS (SELECT resultSets.id FROM resultSets WHERE resultSets.discordSnowflake = ?5 AND NOT resultSets.global LIMIT 1), author (id) AS (SELECT authors.id FROM authors WHERE authors.discordSnowflake = ?4 LIMIT 1) INSERT OR IGNORE INTO resultMappings (resultId, setId, authorId, created, updated) VALUES ((SELECT rollableResult.id FROM rollableResult), (SELECT resultSet.id FROM resultSet), (SELECT author.id FROM author), ?1, ?1);`, parameters: { 'timestamp': { validator: timestamp, index: 1 }, 'tableIdentifier': { validator: tableIdentifierOrId, index: 2 }, 'resultText': { validator: string, index: 3 }, 'userSnowflake': { validator: discordSnowflake, index: 4 }, 'setSnowflake': { validator: discordSnowflake, index: 5 } }, output: nothing() }, getResultMappingsForDiscordSet: { query: `WITH rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier = ?2 OR rollableTableIdentifiers.tableId = ?2 LIMIT 1), visibleSets (id) AS (SELECT resultSets.id FROM resultSets WHERE ((?5 AND resultSets.global) OR resultSets.discordSnowflake = ?4)) SELECT resultMappings.id AS mappingId, rollableResults.id AS resultId, rollableResults.text AS resultText, authors.id AS authorId, COALESCE(authors.name, authorshipTypes.defaultAuthor) AS authorName, authors.url AS authorUrl, authorshipTypes.relationPrefix AS authorRelation, resultSets.id AS setId, resultSets.name AS setName, resultSets.description AS setDescription, resultSets.global AS setGlobal, rollableTables.id AS tableId, rollableTables.identifier AS tableIdentifier, rollableTables.name AS tableName, rollableTables.title AS tableTitle, rollableTables.emoji AS tableEmoji, rollableTables.header AS tableHeader, rollableTables.ordinal AS tableOrdinal, resultMappings.updated AS updated, (CASE WHEN resultMappings.updated = ?1 THEN 'updated' ELSE 'existing' END) AS status FROM resultMappings INNER JOIN rollableResults ON rollableResults.id = resultMappings.resultId LEFT JOIN authors ON authors.id = resultMappings.authorId LEFT JOIN authorshipTypes ON authorshipTypes.id = authors.authorshipTypeId INNER JOIN resultSets ON resultSets.id = resultMappings.setId INNER JOIN rollableTables ON rollableTables.id = rollableResults.tableId WHERE rollableResults.tableId = (SELECT id FROM rollableTable) AND rollableResults.text = ?3 AND resultMappings.setId IN visibleSets ORDER BY (NOT setGlobal) DESC, (authorId IS NOT NULL) DESC, updated, mappingId;`, parameters: { 'timestamp': { validator: nullable(timestamp), index: 1 }, 'tableIdentifier': { validator: tableIdentifierOrId, index: 2 }, 'text': { validator: string, index: 3 }, 'setSnowflake': { validator: discordSnowflake, index: 4 }, 'includeGlobal': { validator: boolean, index: 5 } }, output: rows<{ mappingId: number, resultId: number, resultText: string, authorId: number | null, authorName: string | null, authorUrl: string | null, authorRelation: string | null, setId: number, setName: string | null, setDescription: string | null, setGlobal: number, tableId: number, tableIdentifier: string, tableName: string, tableTitle: string, tableEmoji: string, tableHeader: string, tableOrdinal: number, updated: number, status: 'updated' | 'existing' }>() }, addResultForEditMapping: { query: `WITH rollableTable(id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE (rollableTableIdentifiers.identifier = ?1 OR rollableTableIdentifiers.tableId = ?1) LIMIT 1), oldResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?2 LIMIT 1), targetSet (id) AS (SELECT resultSets.id FROM resultSets WHERE resultSets.discordSnowflake = ?4 LIMIT 1) INSERT OR IGNORE INTO rollableResults (tableId, text) SELECT rollableTable.id, ?3 FROM rollableTable WHERE ?2 != ?3 AND EXISTS (SELECT resultMappings.id FROM resultMappings WHERE resultMappings.resultId = (SELECT oldResult.id FROM oldResult) AND resultMappings.setId = (SELECT targetSet.id FROM targetSet));`, parameters: { 'tableIdentifier': { validator: tableIdentifierOrId, index: 1 }, 'oldText': { validator: string, index: 2 }, 'newText': { validator: string, index: 3 }, 'setSnowflake': { validator: discordSnowflake, index: 4 } }, output: nothing() }, addDiscordAuthorForEditMapping: { query: `WITH authorshipType (id) AS (SELECT authorshipTypes.id FROM authorshipTypes WHERE authorshipTypes.name = 'Discord contributor' LIMIT 1), rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier = ?3 OR rollableTableIdentifiers.tableId = ?3 LIMIT 1), oldResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?4 LIMIT 1), newResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?5 LIMIT 1), targetSet (id) AS (SELECT resultSets.id FROM resultSets WHERE resultSets.discordSnowflake = ?6 LIMIT 1) INSERT INTO authors (name, url, discordSnowflake, discordUsername, authorshipTypeId) SELECT NULL AS name, NULL AS url, ?1 AS discordSnowflake, ?2 AS discordUsername, authorshipType.id AS authorshipTypeId FROM authorshipType WHERE ?4 != ?5 AND EXISTS (SELECT resultMappings.id FROM resultMappings WHERE resultMappings.resultId = (SELECT oldResult.id FROM oldResult) AND resultMappings.setId = (SELECT targetSet.id FROM targetSet)) AND NOT EXISTS (SELECT resultMappings.id FROM resultMappings WHERE resultMappings.resultId = (SELECT newResult.id FROM newResult) AND resultMappings.setId = (SELECT targetSet.id FROM targetSet)) ON CONFLICT DO UPDATE SET discordUsername = ?2;`, parameters: { 'userSnowflake': { validator: discordSnowflake, index: 1 }, 'username': { validator: string, index: 2 }, 'tableIdentifier': { validator: tableIdentifierOrId, index: 3 }, 'oldText': { validator: string, index: 4 }, 'newText': { validator: string, index: 5 }, 'setSnowflake': { validator: discordSnowflake, index: 6 } }, output: nothing() }, editMappingForDiscord: { query: `WITH rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier = ?2 OR rollableTableIdentifiers.tableId = ?2 LIMIT 1), oldResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?3 LIMIT 1), newResult(id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?4 LIMIT 1), author(id) AS (SELECT authors.id FROM authors WHERE authors.discordSnowflake = ?5 LIMIT 1), targetSet(id) AS (SELECT resultSets.id FROM resultSets WHERE resultSets.discordSnowflake = ?6 AND NOT resultSets.global LIMIT 1) UPDATE OR IGNORE resultMappings SET resultId = (SELECT id FROM newResult), authorId = (SELECT id FROM author), updated = ?1 WHERE ?3 != ?4 AND resultMappings.resultId = (SELECT id FROM oldResult) AND resultMappings.setId = (SELECT id FROM targetSet);`, parameters: { 'timestamp': { validator: timestamp, index: 1 }, 'tableIdentifier': { validator: tableIdentifierOrId, index: 2 }, 'oldText': { validator: string, index: 3 }, 'newText': { validator: string, index: 4 }, 'userSnowflake': { validator: discordSnowflake, index: 5 }, 'setSnowflake': { validator: discordSnowflake, index: 6 } }, output: nothing() }, deleteDiscordResultMapping: { query: `WITH rollableTable (id) AS (SELECT rollableTableIdentifiers.tableId FROM rollableTableIdentifiers WHERE rollableTableIdentifiers.identifier = ?1 OR rollableTableIdentifiers.tableId = ?1 LIMIT 1), oldResult (id) AS (SELECT rollableResults.id FROM rollableResults WHERE rollableResults.tableId = (SELECT rollableTable.id FROM rollableTable) AND rollableResults.text = ?2 LIMIT 1), targetSet(id) AS (SELECT resultSets.id FROM resultSets WHERE resultSets.discordSnowflake = ?3 AND NOT resultSets.global LIMIT 1) DELETE FROM resultMappings WHERE resultId = (SELECT oldResult.id FROM oldResult) AND setId = (SELECT targetSet.id FROM targetSet);`, parameters: { 'tableIdentifier': { validator: tableIdentifierOrId, index: 1 }, 'text': { validator: string, index: 2 }, 'setSnowflake': { validator: discordSnowflake, index: 3 } }, output: writeCount() }, getDiscordAuthor: { query: ` SELECT authors.id AS id, COALESCE(authors.name, authorshipTypes.defaultAuthor) AS name, authors.url AS url, authorshipTypes.relationPrefix AS relation FROM authors INNER JOIN main.authorshipTypes authorshipTypes on authorshipTypes.id = authors.authorshipTypeId WHERE authors.discordSnowflake = ?1;`, parameters: { 'userSnowflake': { validator: discordSnowflake, index: 1 } }, output: singleton<{ id: number, name: string, url: string, relation: string }>() }, setDiscordAuthor: { query: ` INSERT INTO authors (discordSnowflake, discordUsername, name, url, authorshipTypeId) VALUES (?1, ?2, ?3, ?4, (SELECT authorshipTypes.id FROM authorshipTypes WHERE authorshipTypes.name = 'Discord contributor')) ON CONFLICT DO UPDATE SET discordUsername = ?2, name = ?3, url = ?4;`, parameters: { 'userSnowflake': { validator: discordSnowflake, index: 1 }, 'username': { validator: string, index: 2 }, 'name': { validator: nullable(string), index: 3 }, 'url': { validator: nullable(URL), index: 4 } }, output: nothing() }, getTableIdsByIdentifierOrHeader: { query: `SELECT COALESCE(rollableTableIdentifiers.tableId, rollableTableHeaders.tableId) AS id FROM json_each(?1) selection LEFT JOIN rollableTableIdentifiers ON rollableTableIdentifiers.identifier = selection.value LEFT JOIN rollableTableHeaders ON rollableTableHeaders.header = selection.value;`, parameters: { 'identifiersOrHeaders': { validator: jsonArray, index: 1 } }, output: rows(extract("id")) }, getTables: { query: `SELECT id, identifier, name, title, emoji, header, ordinal FROM rollableTables`, parameters: {}, output: rows<{ id: number, identifier: string, name: string, title: string, emoji: string, header: string, ordinal: number }>() }, getFullDatabaseForDiscordSet: { query: `WITH visibleSets (id, name, description, global) AS (SELECT resultSets.id, resultSets.name, resultSets.description, resultSets.global FROM resultSets WHERE (resultSets.global OR resultSets.discordSnowflake = ?1)), visibleResults (mappingId, setId, textId, tableId, text, authorId, updated) AS (SELECT resultMappings.id AS mappingId, resultMappings.setId AS setId, resultMappings.resultId AS textId, rollableResults.tableId AS tableId, rollableResults.text AS text, resultMappings.authorId AS authorId, resultMappings.updated AS updated FROM resultMappings INNER JOIN visibleSets ON resultMappings.setId = visibleSets.id INNER JOIN rollableResults ON rollableResults.id = resultMappings.resultId), visibleAuthors (id, name, url, relation) AS (SELECT DISTINCT authors.id, COALESCE(authors.name, authorshipTypes.defaultAuthor), authors.url, authorshipTypes.relationPrefix FROM visibleResults INNER JOIN authors ON authors.id = visibleResults.authorId INNER JOIN authorshipTypes ON authorshipTypes.id = authors.authorshipTypeId) SELECT (SELECT json_group_array(json_object('id', visibleSets.id, 'name', visibleSets.name, 'description', visibleSets.description, 'global', CASE WHEN visibleSets.global THEN json('true') ELSE json('false') END)) FROM visibleSets) AS sets, (SELECT json_group_array(json_object('id', visibleAuthors.id, 'name', visibleAuthors.name, 'url', visibleAuthors.url, 'relation', visibleAuthors.relation)) FROM visibleAuthors) AS authors, (SELECT json_group_array(json_object('mappingId', visibleResults.mappingId, 'textId', visibleResults.textId, 'text', visibleResults.text, 'tableId', visibleResults.tableId, 'setId', visibleResults.setId, 'authorId', visibleResults.authorId, 'updated', visibleResults.updated)) FROM visibleResults) AS mappings;`, parameters: { 'setSnowflake': { validator: nullable(discordSnowflake), index: 1 } }, output: guaranteedSingleton(jsonParser<{ sets: { id: number, name: string | null, description: string | null, global: boolean }[], authors: { id: number, name: string, url: string | null, relation: string }[], mappings: { mappingId: number, textId: number, text: string, tableId: number, setId: number, authorId: number, updated: number }[], }>(['sets', 'authors', 'mappings'])) } } as const satisfies QueryDefinitions);