Skip to content

Commit

Permalink
migrate from DBI to Sequel
Browse files Browse the repository at this point in the history
* Store now expects a Sequel::Database object
* SquishSelect and SquishAssert refactored
* SquishQuery now keeps raw unescaped literals in @Strings, SquishAssert
  passes these as is to Sequel::Dataset, SquishSelect still escapes them
  back into the SQL query locally
* validate_expression now returns the validated string for chainability
* substitute_parameters dropped: Sequel understands named parameters
* SquishSelect#to_sql (and by extention Store#select) now returns only
  SQL query (same params hash can now be passed to Sequel verbatim)
* SquishSelect#to_sql now names columns with corresponding blank node
  names where applicable
* unit test now runs select and assert on an in-memory Sqlite database
  • Loading branch information
angdraug committed Nov 8, 2011
1 parent ef6717c commit e8004f1
Show file tree
Hide file tree
Showing 9 changed files with 580 additions and 392 deletions.
70 changes: 26 additions & 44 deletions doc/examples/samizdat-rdf-config.yaml
Expand Up @@ -21,54 +21,36 @@ ns:
# properties, so at least these three and s::id must be mapped.
#
map:
's::id': {Resource: id}
'dc::date': {Resource: published_date}
'dct::isPartOf': {Resource: part_of}
's::isPartOfSubProperty': {Resource: part_of_subproperty}
's::partSequenceNumber': {Resource: part_sequence_number}
's::id': {resource: id}
'dc::date': {resource: published_date}
'dct::isPartOf': {resource: part_of}
's::isPartOfSubProperty': {resource: part_of_subproperty}
's::partSequenceNumber': {resource: part_sequence_number}

'rdf::subject': {Statement: subject}
'rdf::predicate': {Statement: predicate}
'rdf::object': {Statement: object}
'rdf::subject': {statement: subject}
'rdf::predicate': {statement: predicate}
'rdf::object': {statement: object}

's::login': {Member: login}
's::fullName': {Member: full_name}
's::email': {Member: email}
's::login': {member: login}
's::fullName': {member: full_name}
's::email': {member: email}

'dc::title': {Message: title}
'dc::creator': {Message: creator}
'dc::format': {Message: format}
'dc::language': {Message: language}
's::openForAll': {Message: open}
's::hidden': {Message: hidden}
's::locked': {Message: locked}
's::content': {Message: content}
's::htmlFull': {Message: html_full}
's::htmlShort': {Message: html_short}
'dc::title': {message: title}
'dc::creator': {message: creator}
'dc::format': {message: format}
'dc::language': {message: language}
's::openForAll': {message: open}
's::hidden': {message: hidden}
's::locked': {message: locked}
's::content': {message: content}
's::htmlFull': {message: html_full}
's::htmlShort': {message: html_short}

's::rating': {Statement: rating}
's::rating': {statement: rating}

's::voteProposition': {Vote: proposition}
's::voteMember': {Vote: member}
's::voteRating': {Vote: rating}

'items::description': {Item: description}
'items::contributor': {Item: contributor}
'items::possessor': {Item: possessor}

'items::item': {Possession: item}
'items::takenFrom': {Possession: taken_from}
'items::givenTo': {Possession: given_to}

'dc::description': {Event: description}
'ical::dtstart': {Event: dtstart}
'ical::dtend': {Event: dtend}
's::rruleEvent': {Recurrence: event}
'ical::freq': {Recurrence: freq}
'ical::interval': {Recurrence: interval}
'ical::until': {Recurrence: until}
'ical::byhour': {Recurrence: byhour}
'ical::byday': {Recurrence: byday}
's::voteProposition': {vote: proposition}
's::voteMember': {vote: member}
's::voteRating': {vote: rating}

# Map of properties into lists of their subproperties. For each property
# listed here, an additional qualifier field named <field>_subproperty
Expand All @@ -91,5 +73,5 @@ subproperties:
# in the RDF graph.
#
transitive_closure:
'dct::isPartOf': Part
'dct::isPartOf': part

138 changes: 57 additions & 81 deletions doc/examples/samizdat-triggers-pgsql.sql
@@ -1,6 +1,6 @@
-- Samizdat Database Triggers - PostgreSQL
--
-- Copyright (c) 2002-2009 Dmitry Borodaenko <angdraug@debian.org>
-- Copyright (c) 2002-2011 Dmitry Borodaenko <angdraug@debian.org>
--
-- This program is free software.
-- You can distribute/modify this program under the terms of
Expand All @@ -10,70 +10,46 @@
CREATE FUNCTION insert_resource() RETURNS TRIGGER AS $$
BEGIN
IF NEW.id IS NULL THEN
INSERT INTO Resource (literal, uriref, label)
INSERT INTO resource (literal, uriref, label)
VALUES ('false', 'false', TG_ARGV[0]);
NEW.id := currval('Resource_id_seq');
NEW.id := currval('resource_id_seq');
END IF;
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER insert_statement BEFORE INSERT ON Statement
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Statement');
CREATE TRIGGER insert_statement BEFORE INSERT ON statement
FOR EACH ROW EXECUTE PROCEDURE insert_resource('statement');

CREATE TRIGGER insert_member BEFORE INSERT ON Member
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Member');
CREATE TRIGGER insert_member BEFORE INSERT ON member
FOR EACH ROW EXECUTE PROCEDURE insert_resource('member');

CREATE TRIGGER insert_message BEFORE INSERT ON Message
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Message');
CREATE TRIGGER insert_message BEFORE INSERT ON message
FOR EACH ROW EXECUTE PROCEDURE insert_resource('message');

CREATE TRIGGER insert_vote BEFORE INSERT ON Vote
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Vote');

CREATE TRIGGER insert_item BEFORE INSERT ON Item
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Item');

CREATE TRIGGER insert_possession BEFORE INSERT ON Possession
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Possession');

CREATE TRIGGER insert_event BEFORE INSERT ON Event
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Event');

CREATE TRIGGER insert_recurrence BEFORE INSERT ON Recurrence
FOR EACH ROW EXECUTE PROCEDURE insert_resource('Recurrence');
CREATE TRIGGER insert_vote BEFORE INSERT ON vote
FOR EACH ROW EXECUTE PROCEDURE insert_resource('vote');

CREATE FUNCTION delete_resource() RETURNS TRIGGER AS $$
BEGIN
DELETE FROM Resource WHERE id = OLD.id;
DELETE FROM resource WHERE id = OLD.id;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER delete_statement AFTER DELETE ON Statement
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_member AFTER DELETE ON Member
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_message AFTER DELETE ON Message
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_vote AFTER DELETE ON Vote
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_item AFTER DELETE ON Item
CREATE TRIGGER delete_statement AFTER DELETE ON statement
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_possession AFTER DELETE ON Possession
CREATE TRIGGER delete_member AFTER DELETE ON member
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_event AFTER DELETE ON Event
CREATE TRIGGER delete_message AFTER DELETE ON message
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE TRIGGER delete_recurrence AFTER DELETE ON Recurrence
CREATE TRIGGER delete_vote AFTER DELETE ON vote
FOR EACH ROW EXECUTE PROCEDURE delete_resource();

CREATE FUNCTION select_subproperty(value Resource.id%TYPE, subproperty Resource.id%TYPE) RETURNS Resource.id%TYPE AS $$
CREATE FUNCTION select_subproperty(value resource.id%TYPE, subproperty resource.id%TYPE) RETURNS resource.id%TYPE AS $$
BEGIN
IF subproperty IS NULL THEN
RETURN NULL;
Expand All @@ -83,61 +59,61 @@ CREATE FUNCTION select_subproperty(value Resource.id%TYPE, subproperty Resource.
END;
$$ LANGUAGE 'plpgsql';

CREATE FUNCTION calculate_statement_rating(statement_id Statement.id%TYPE) RETURNS Statement.rating%TYPE AS $$
CREATE FUNCTION calculate_statement_rating(statement_id statement.id%TYPE) RETURNS statement.rating%TYPE AS $$
BEGIN
RETURN (SELECT AVG(rating) FROM Vote WHERE proposition = statement_id);
RETURN (SELECT AVG(rating) FROM vote WHERE proposition = statement_id);
END;
$$ LANGUAGE 'plpgsql';

CREATE FUNCTION update_nrelated(tag_id Resource.id%TYPE) RETURNS VOID AS $$
CREATE FUNCTION update_nrelated(tag_id resource.id%TYPE) RETURNS VOID AS $$
DECLARE
dc_relation Resource.label%TYPE := 'http://purl.org/dc/elements/1.1/relation';
s_subtag_of Resource.label%TYPE := 'http://www.nongnu.org/samizdat/rdf/schema#subTagOf';
s_subtag_of_id Resource.id%TYPE;
n Tag.nrelated%TYPE;
dc_relation resource.label%TYPE := 'http://purl.org/dc/elements/1.1/relation';
s_subtag_of resource.label%TYPE := 'http://www.nongnu.org/samizdat/rdf/schema#subTagOf';
s_subtag_of_id resource.id%TYPE;
n tag.nrelated%TYPE;
supertag RECORD;
BEGIN
-- update nrelated
SELECT COUNT(*) INTO n
FROM Statement s
INNER JOIN Resource p ON s.predicate = p.id
FROM statement s
INNER JOIN resource p ON s.predicate = p.id
WHERE p.label = dc_relation AND s.object = tag_id AND s.rating > 0;

UPDATE Tag SET nrelated = n WHERE id = tag_id;
UPDATE tag SET nrelated = n WHERE id = tag_id;
IF NOT FOUND THEN
INSERT INTO Tag (id, nrelated) VALUES (tag_id, n);
INSERT INTO tag (id, nrelated) VALUES (tag_id, n);
END IF;

-- update nrelated_with_subtags for this tag and its supertags
SELECT id INTO s_subtag_of_id FROM Resource
SELECT id INTO s_subtag_of_id FROM resource
WHERE label = s_subtag_of;

FOR supertag IN (
SELECT tag_id AS id, 0 AS distance
UNION
SELECT part_of AS id, distance FROM Part
SELECT part_of AS id, distance FROM part
WHERE id = tag_id
AND part_of_subproperty = s_subtag_of_id
ORDER BY distance ASC)
LOOP
UPDATE Tag
UPDATE tag
SET nrelated_with_subtags = nrelated + COALESCE((
SELECT SUM(subt.nrelated)
FROM Part p
INNER JOIN Tag subt ON subt.id = p.id
FROM part p
INNER JOIN tag subt ON subt.id = p.id
WHERE p.part_of = supertag.id
AND p.part_of_subproperty = s_subtag_of_id), 0)
WHERE id = supertag.id;
END LOOP;
END;
$$ LANGUAGE 'plpgsql';

CREATE FUNCTION update_nrelated_if_subtag(tag_id Resource.id%TYPE, property Resource.id%TYPE) RETURNS VOID AS $$
CREATE FUNCTION update_nrelated_if_subtag(tag_id resource.id%TYPE, property resource.id%TYPE) RETURNS VOID AS $$
DECLARE
s_subtag_of Resource.label%TYPE := 'http://www.nongnu.org/samizdat/rdf/schema#subTagOf';
s_subtag_of_id Resource.id%TYPE;
s_subtag_of resource.label%TYPE := 'http://www.nongnu.org/samizdat/rdf/schema#subTagOf';
s_subtag_of_id resource.id%TYPE;
BEGIN
SELECT id INTO s_subtag_of_id FROM Resource
SELECT id INTO s_subtag_of_id FROM resource
WHERE label = s_subtag_of;

IF property = s_subtag_of_id THEN
Expand All @@ -148,22 +124,22 @@ $$ LANGUAGE 'plpgsql';

CREATE FUNCTION update_rating() RETURNS TRIGGER AS $$
DECLARE
dc_relation Resource.label%TYPE := 'http://purl.org/dc/elements/1.1/relation';
old_rating Statement.rating%TYPE;
new_rating Statement.rating%TYPE;
tag_id Resource.id%TYPE;
predicate_uriref Resource.label%TYPE;
dc_relation resource.label%TYPE := 'http://purl.org/dc/elements/1.1/relation';
old_rating statement.rating%TYPE;
new_rating statement.rating%TYPE;
tag_id resource.id%TYPE;
predicate_uriref resource.label%TYPE;
BEGIN
-- save some values for later reference
SELECT s.rating, s.object, p.label
INTO old_rating, tag_id, predicate_uriref
FROM Statement s
INNER JOIN Resource p ON s.predicate = p.id
FROM statement s
INNER JOIN resource p ON s.predicate = p.id
WHERE s.id = NEW.proposition;

-- set new rating of the proposition
new_rating := calculate_statement_rating(NEW.proposition);
UPDATE Statement SET rating = new_rating WHERE id = NEW.proposition;
UPDATE statement SET rating = new_rating WHERE id = NEW.proposition;

-- check if new rating reverts truth value of the proposition
IF predicate_uriref = dc_relation
Expand All @@ -177,7 +153,7 @@ CREATE FUNCTION update_rating() RETURNS TRIGGER AS $$
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER update_rating AFTER INSERT OR UPDATE OR DELETE ON Vote
CREATE TRIGGER update_rating AFTER INSERT OR UPDATE OR DELETE ON vote
FOR EACH ROW EXECUTE PROCEDURE update_rating();

CREATE FUNCTION before_update_part() RETURNS TRIGGER AS $$
Expand All @@ -197,7 +173,7 @@ CREATE FUNCTION before_update_part() RETURNS TRIGGER AS $$

-- check for loops
IF NEW.part_of = NEW.id OR NEW.part_of IN (
SELECT id FROM Part WHERE part_of = NEW.id)
SELECT id FROM part WHERE part_of = NEW.id)
THEN
-- unset part_of, but don't fail whole query
NEW.part_of = NULL;
Expand All @@ -215,7 +191,7 @@ CREATE FUNCTION before_update_part() RETURNS TRIGGER AS $$
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER before_update_part BEFORE INSERT OR UPDATE ON Resource
CREATE TRIGGER before_update_part BEFORE INSERT OR UPDATE ON resource
FOR EACH ROW EXECUTE PROCEDURE before_update_part();

CREATE FUNCTION after_update_part() RETURNS TRIGGER AS $$
Expand All @@ -236,37 +212,37 @@ CREATE FUNCTION after_update_part() RETURNS TRIGGER AS $$
IF TG_OP != 'INSERT' THEN
IF OLD.part_of IS NOT NULL THEN
-- clean up links generated for old part_of
DELETE FROM Part
DELETE FROM part
WHERE id IN (
-- for old resource...
SELECT OLD.id
UNION
--...and all its parts, ...
SELECT id FROM Part WHERE part_of = OLD.id)
SELECT id FROM part WHERE part_of = OLD.id)
AND part_of IN (
-- ...remove links to all parents of old resource
SELECT part_of FROM Part WHERE id = OLD.id)
SELECT part_of FROM part WHERE id = OLD.id)
AND part_of_subproperty = OLD.part_of_subproperty;
END IF;
END IF;

IF TG_OP != 'DELETE' THEN
IF NEW.part_of IS NOT NULL THEN
-- generate links to the parent and grand-parents of new resource
INSERT INTO Part (id, part_of, part_of_subproperty, distance)
INSERT INTO part (id, part_of, part_of_subproperty, distance)
SELECT NEW.id, NEW.part_of, NEW.part_of_subproperty, 1
UNION
SELECT NEW.id, part_of, NEW.part_of_subproperty, distance + 1
FROM Part
FROM part
WHERE id = NEW.part_of
AND part_of_subproperty = NEW.part_of_subproperty;

-- generate links from all parts of new resource to all its parents
INSERT INTO Part (id, part_of, part_of_subproperty, distance)
INSERT INTO part (id, part_of, part_of_subproperty, distance)
SELECT child.id, parent.part_of, NEW.part_of_subproperty,
child.distance + parent.distance
FROM Part child
INNER JOIN Part parent
FROM part child
INNER JOIN part parent
ON parent.id = NEW.id
AND parent.part_of_subproperty = NEW.part_of_subproperty
WHERE child.part_of = NEW.id
Expand All @@ -286,5 +262,5 @@ CREATE FUNCTION after_update_part() RETURNS TRIGGER AS $$
END;
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER after_update_part AFTER INSERT OR UPDATE OR DELETE ON Resource
CREATE TRIGGER after_update_part AFTER INSERT OR UPDATE OR DELETE ON resource
FOR EACH ROW EXECUTE PROCEDURE after_update_part();

0 comments on commit e8004f1

Please sign in to comment.