summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormorkt <morkt@users.noreply.github.com>2015-11-10 06:06:54 +0400
committermorkt <morkt@users.noreply.github.com>2015-11-10 06:06:54 +0400
commitd488967ebdde36bf2c959f1d4e1c55c622887bf8 (patch)
treedeed89549ba634260924c932021be0de1796d62e
parentddbf3ae0ae530954b0e105e99819c85cfe4de17c (diff)
parent052b78e84b4c7379f107cfaa5f9cede09b8b1b7b (diff)
Merge branch 'master' into poll
-rw-r--r--.gitignore1
-rw-r--r--Makefile7
-rw-r--r--data/config_example.pl5
-rw-r--r--data/docs/421
-rw-r--r--data/docs/4.cs1
-rw-r--r--data/docs/4.de1
-rw-r--r--data/docs/4.hu1
-rw-r--r--data/docs/4.it1
-rw-r--r--data/docs/4.ru1
-rw-r--r--data/docs/522
-rw-r--r--data/docs/5.cs1
-rw-r--r--data/docs/5.de1
-rw-r--r--data/docs/5.hu1
-rw-r--r--data/docs/5.it1
-rw-r--r--data/docs/5.ru1
-rw-r--r--data/docs/626
-rw-r--r--data/docs/6.cs1
-rw-r--r--data/docs/6.de1
-rw-r--r--data/docs/6.hu1
-rw-r--r--data/docs/6.it1
-rw-r--r--data/docs/6.ru1
-rw-r--r--data/docs/6.tr3
-rw-r--r--data/global.pl7
-rw-r--r--data/js/dropdownsearch.js28
-rw-r--r--data/js/iv.js22
-rw-r--r--data/js/lib.js2
-rw-r--r--data/js/misc.js21
-rw-r--r--data/js/vnscr.js2
-rw-r--r--data/lang.txt24
-rw-r--r--data/style.css10
-rw-r--r--lib/Multi/API.pm182
-rw-r--r--lib/Multi/Anime.pm4
-rw-r--r--lib/Multi/Feed.pm16
-rw-r--r--lib/Multi/IRC.pm73
-rw-r--r--lib/Multi/Maintenance.pm31
-rw-r--r--lib/Multi/RG.pm20
-rw-r--r--lib/VNDB/DB/Affiliates.pm6
-rw-r--r--lib/VNDB/DB/Chars.pm176
-rw-r--r--lib/VNDB/DB/Discussions.pm28
-rw-r--r--lib/VNDB/DB/Misc.pm101
-rw-r--r--lib/VNDB/DB/Producers.pm91
-rw-r--r--lib/VNDB/DB/Releases.pm198
-rw-r--r--lib/VNDB/DB/Staff.pm161
-rw-r--r--lib/VNDB/DB/Tags.pm6
-rw-r--r--lib/VNDB/DB/ULists.pm48
-rw-r--r--lib/VNDB/DB/VN.pm225
-rw-r--r--lib/VNDB/Func.pm4
-rw-r--r--lib/VNDB/Handler/Chars.pm15
-rw-r--r--lib/VNDB/Handler/Misc.pm7
-rw-r--r--lib/VNDB/Handler/Producers.pm21
-rw-r--r--lib/VNDB/Handler/Releases.pm19
-rw-r--r--lib/VNDB/Handler/Staff.pm28
-rw-r--r--lib/VNDB/Handler/Users.pm9
-rw-r--r--lib/VNDB/Handler/VNEdit.pm24
-rw-r--r--lib/VNDB/Handler/VNPage.pm20
-rw-r--r--lib/VNDB/Util/Auth.pm23
-rw-r--r--lib/VNDB/Util/BrowseHTML.pm4
-rw-r--r--lib/VNDB/Util/CommonHTML.pm30
-rw-r--r--lib/VNDB/Util/FormHTML.pm4
-rw-r--r--lib/VNDB/Util/LayoutHTML.pm13
-rw-r--r--util/sql/all.sql6
-rw-r--r--util/sql/func.sql768
-rw-r--r--util/sql/schema.sql530
-rw-r--r--util/sql/tableattrs.sql189
-rw-r--r--util/sql/triggers.sql41
-rwxr-xr-xutil/sqleditfunc.pl112
-rw-r--r--util/updates/update_2.25-sqlsplit.sql258
-rw-r--r--util/updates/update_2.26.sql9
68 files changed, 2031 insertions, 1685 deletions
diff --git a/.gitignore b/.gitignore
index 356f07e8..79be4a79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,5 @@
/static/st
/static/robots.txt
/static/api
+/util/sql/editfunc.sql
/www/
diff --git a/Makefile b/Makefile
index 60023928..d914d504 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,7 @@
update-2.10 update-2.11 update-2.12 update-2.13 update-2.14 update-2.15 update-2.16 update-2.17\
update-2.18 update-2.19 update-2.20 update-2.21 update-2.22 update-2.23
-all: dirs js skins robots data/config.pl
+all: dirs js skins robots data/config.pl util/sql/editfunc.sql
dirs: static/f/js static/ch static/cv static/sf static/st data/log www www/feeds www/api
@@ -60,6 +60,9 @@ skins: $(shell ls static/s | sed -e 's/\(.\+\)/static\/s\/\1\/style.css/g')
robots: dirs www/robots.txt static/robots.txt
+util/sql/editfunc.sql: util/sqleditfunc.pl util/sql/schema.sql
+ util/sqleditfunc.pl
+
static/ch static/cv static/sf static/st:
mkdir $@;
for i in $$(seq -w 0 1 99); do mkdir "$@/$$i"; done
@@ -138,7 +141,7 @@ define runpsql
endef
-sql-import:
+sql-import: util/sql/editfunc.sql
${runpsql} < util/sql/all.sql
diff --git a/data/config_example.pl b/data/config_example.pl
index b1e405ce..96f003fc 100644
--- a/data/config_example.pl
+++ b/data/config_example.pl
@@ -17,9 +17,8 @@ package VNDB;
%S,
url => 'http://your.site.root/',
url_static => 'http://your.static.site.root/',
- global_salt => '<some long unique string>',
- form_salt => '<another unique string>',
- scrypt_salt => '<yet another unique string>',
+ form_salt => '<some unique string>',
+ scrypt_salt => '<another unique string>',
);
diff --git a/data/docs/4 b/data/docs/4
index fc383e74..05d20a56 100644
--- a/data/docs/4
+++ b/data/docs/4
@@ -3,14 +3,19 @@
:SUB:When to add a producer
-<p>
- A producer entry should be created when adding a release of a visual novel made by a
- producer not yet listed in the database. Producers that don't have any releases listed
- in the database may be deleted after a while, so only create an entry if you're going to
- link it to a release. For a producer to be listed, they're required to have at least
- released one completed product. Which means that for translation projects, only add the
- group as a producer when they've actually finished a project.
-</p>
+<p>A producer entry may be created in one of the following scenarios:</p>
+<ol>
+<li>When it is needed to link the developer or publisher to a release entry.
+In this case, the producer is required to have released at least one completed
+product. This means that translation projects and starting developers may only
+be added when they've actually finished a project.</li>
+<li>When it is needed to link two or more producer entries together to provide
+a correct view of the relations between producers. The producer entries that
+are being linked together should qualify for the first criteria, and need to be
+linked to at least one release entry.</li>
+</ol>
+<p>Producer entries that do not qualify for either rule may be deleted after a
+while.</p>
:SUB:General info
diff --git a/data/docs/4.cs b/data/docs/4.cs
index f943274f..d53d939f 100644
--- a/data/docs/4.cs
+++ b/data/docs/4.cs
@@ -1,4 +1,5 @@
:TITLE:Přidání/editace producenta
+:INC:incomplete
:INC:index
diff --git a/data/docs/4.de b/data/docs/4.de
index 929b9a13..9f977abd 100644
--- a/data/docs/4.de
+++ b/data/docs/4.de
@@ -1,4 +1,5 @@
:TITLE:Hersteller hinzufügen/bearbeiten
+:INC:incomplete
:INC:index
diff --git a/data/docs/4.hu b/data/docs/4.hu
index bce222e5..6df976bd 100644
--- a/data/docs/4.hu
+++ b/data/docs/4.hu
@@ -1,4 +1,5 @@
:TITLE:Készítők bejegyzése/szerkesztése
+:INC:incomplete
:INC:index
diff --git a/data/docs/4.it b/data/docs/4.it
index cd809353..5963f89f 100644
--- a/data/docs/4.it
+++ b/data/docs/4.it
@@ -1,4 +1,5 @@
:TITLE:Aggiungere/Modificare un Editore
+:INC:incomplete
:INC:index
diff --git a/data/docs/4.ru b/data/docs/4.ru
index 974d11c5..c792a224 100644
--- a/data/docs/4.ru
+++ b/data/docs/4.ru
@@ -1,4 +1,5 @@
:TITLE:Добавление/Редактирование компании
+:INC:incomplete
:INC:index
diff --git a/data/docs/5 b/data/docs/5
index 90254c22..e817a868 100644
--- a/data/docs/5
+++ b/data/docs/5
@@ -7,9 +7,9 @@
The main audience of VNDB are the English speaking fans of visual novels. In
creating the database, we assume the intended audience can't read or recognise
anything not written in the roman script. Therefore, romanization is applied
- to many fields in the database: The main title of visual novels, releases and
- producers should all be properly romanized if they arent in roman script
- already.<br />
+ to many fields in the database: The main title of visual novels, releases,
+ producers and characters should all be properly romanized if they arent in
+ roman script already.<br />
To keep the database consistent, we have decided to use a mix of <a
href="http://en.wikipedia.org/wiki/Hepburn_romanization">Hepburn</a> and <a
href="http://en.wikipedia.org/wiki/Wapuro">Wapuro</a> for all fields. This is
@@ -24,15 +24,15 @@
:SUB:Capitalization
<p>
- It occasionally happens that official titles or names are either entirely in
- uppercase or lowercase characters. If there is no real reason for this choice
- of capitalization (e.g. it's not an acronym), these titles and names should be
- properly converted to normal English capitalization (as described
- <a href="http://en.wikipedia.org/wiki/Capitalization">on Wikipedia</a>) before
+ It occasionally happens that official titles are either entirely in uppercase
+ or lowercase characters. If there is no real reason for this choice of
+ capitalization (e.g. it's not an acronym), these titles should be properly
+ converted to normal English capitalization (as described <a
+ href="http://en.wikipedia.org/wiki/Capitalization">on Wikipedia</a>) before
being entered in the database.<br />
- This only applies to the romanized titles and the main title of a visual novel
- entry, the 'Original title' field of producer and release entries should still
- use the official capitalization.
+ This only applies to the main title of a visual novel entry, the 'Original
+ title' field and the title/name fields of other database entries (including
+ release entries) should use the official capitalization wherever possible.
</p>
diff --git a/data/docs/5.cs b/data/docs/5.cs
index 49f94c1f..90c660ad 100644
--- a/data/docs/5.cs
+++ b/data/docs/5.cs
@@ -1,4 +1,5 @@
:TITLE:Doporučení k editacím
+:INC:incomplete
:INC:index
diff --git a/data/docs/5.de b/data/docs/5.de
index fcbe5572..8b45d636 100644
--- a/data/docs/5.de
+++ b/data/docs/5.de
@@ -1,4 +1,5 @@
:TITLE:Bearbeitungsrichtlinien
+:INC:incomplete
:INC:index
diff --git a/data/docs/5.hu b/data/docs/5.hu
index b6035ed6..72e01b27 100644
--- a/data/docs/5.hu
+++ b/data/docs/5.hu
@@ -1,4 +1,5 @@
:TITLE:Szerkesztési útmutató
+:INC:incomplete
:INC:index
diff --git a/data/docs/5.it b/data/docs/5.it
index 15593321..b783be10 100644
--- a/data/docs/5.it
+++ b/data/docs/5.it
@@ -1,4 +1,5 @@
:TITLE:Guida alle modifiche
+:INC:incomplete
:INC:index
diff --git a/data/docs/5.ru b/data/docs/5.ru
index 7cceb0ad..d234f926 100644
--- a/data/docs/5.ru
+++ b/data/docs/5.ru
@@ -1,4 +1,5 @@
:TITLE:Рекомендации по правке
+:INC:incomplete
:INC:index
diff --git a/data/docs/6 b/data/docs/6
index 8bb65e7e..2e88f1bc 100644
--- a/data/docs/6
+++ b/data/docs/6
@@ -12,11 +12,8 @@
have an effect on the story, thus playing a visual novel a second time while
giving other answers may result in an entirely different plot.<br />
<br />
- For more information see <a href="http://en.wikipedia.org/wiki/Visual_Novel">
- the Wikipedia article on visual novels</a> or the description on
- <a href="http://visual-novels.net/vn/index.php?option=com_content&amp;task=view&amp;id=259&amp;Itemid=47">Visual-Novels.net</a>.
- To get a general idea of the genre, try one of the free short visual novels from
- <a href="http://altogether.insani.org/">al|together</a>.
+ To get a general idea of the genre, try one of the free short visual novels
+ from <a href="http://altogether.insani.org/">al|together</a>.
</p>
@@ -49,12 +46,11 @@
:SUB:How can I help VNDB?
<p>
- There are many ways to contribute to VNDB. First of all you can freely
- edit all information found on this website, so if you find any errors
- just click the "edit" link on the top right of the page. You can also
- add new information (visual novels, producers, releases) to the database,
- though please search the database before you do in order to prevent
- duplicate pages.<br />
+ There are many ways to contribute to VNDB. First of all you can freely edit
+ all information found on this website, so if you find any errors just click
+ the "edit" link on the top right of the page. You can also add new information
+ to the database, though please search the database before you do in order to
+ prevent duplicate pages.<br />
<br />
To discuss about new features or to help the development of the website
itself, feel free to participate on the <a href="/t">discussion board</a>
@@ -82,9 +78,10 @@
If you're interested in VNs that are generally considered "good" or "awesome",
check out the <a href="/v/all?s=rating;o=d">ratings</a>. If you're looking for
something specific, <a href="/g">browse through the tags</a>. If you're
- interested in a more personal answer, you can compare the vote lists of our <a
- href="/u/all">users</a> with your own and decide what to read next based on
- that.
+ interested in a more personal answer, you can ask on <a
+ href="https://www.reddit.com/r/visualnovelsuggest">/r/visualnovelsuggest</a> or
+ you can compare the vote lists of our <a href="/u/all">users</a> with your own
+ and decide what to read next based on that.
</p>
@@ -124,4 +121,3 @@
If you feel these banners don't suit your needs, please don't hesitate to make
one yourself.
</p>
-
diff --git a/data/docs/6.cs b/data/docs/6.cs
index 1325d24b..4569c233 100644
--- a/data/docs/6.cs
+++ b/data/docs/6.cs
@@ -1,4 +1,5 @@
:TITLE:Frequently Asked Questions
+:INC:incomplete
:INC:index
diff --git a/data/docs/6.de b/data/docs/6.de
index 72e8a8f7..b3a87c70 100644
--- a/data/docs/6.de
+++ b/data/docs/6.de
@@ -1,4 +1,5 @@
:TITLE:Häufig gestellte Fragen
+:INC:incomplete
:INC:index
diff --git a/data/docs/6.hu b/data/docs/6.hu
index a01f6d37..c39da1b2 100644
--- a/data/docs/6.hu
+++ b/data/docs/6.hu
@@ -1,4 +1,5 @@
:TITLE:Gyakran Ismételt Kérdések
+:INC:incomplete
:INC:index
diff --git a/data/docs/6.it b/data/docs/6.it
index 8002089c..509de0fb 100644
--- a/data/docs/6.it
+++ b/data/docs/6.it
@@ -1,4 +1,5 @@
:TITLE:Domande frequenti
+:INC:incomplete
:INC:index
diff --git a/data/docs/6.ru b/data/docs/6.ru
index 0f9d3243..ee5633cc 100644
--- a/data/docs/6.ru
+++ b/data/docs/6.ru
@@ -1,4 +1,5 @@
:TITLE:Часто задаваемые вопросы
+:INC:incomplete
:INC:index
diff --git a/data/docs/6.tr b/data/docs/6.tr
index bcafca4f..e3f6b207 100644
--- a/data/docs/6.tr
+++ b/data/docs/6.tr
@@ -1,4 +1,5 @@
:TITLE:Sıkça Sorulan Sorular
+:INC:incomplete
:INC:index
@@ -68,4 +69,4 @@ Elbette! Hatta VNDB'yi tanıtmakta kullanabileceğiniz birkaç simge de hazırla
<img src="http://static.yorhel.nl/2009/vndb5.jpg" style="margin: 5px">
<img src="http://static.yorhel.nl/2009/vndb6.jpg" style="margin: 5px"><br />
Eğer bunlar ihtiyacınızı karşılamıyorsa, kendiniz bir tane yapmaktan çekinmeyin lütfen.
-</p> \ No newline at end of file
+</p>
diff --git a/data/global.pl b/data/global.pl
index a11c6869..29f1a6c9 100644
--- a/data/global.pl
+++ b/data/global.pl
@@ -21,8 +21,7 @@ our %S;
url => 'http://vndb.org', # Only used by Multi, web pages infer their own address
url_static => 'http://s.vndb.org',
skin_default => 'angel',
- global_salt => 'any-private-string-here',
- form_salt => 'a-different-private-string-here',
+ form_salt => 'a-private-string-here',
scrypt_args => [ 65536, 8, 1 ], # N, r, p
scrypt_salt => 'another-random-string',
regen_static => 0,
@@ -32,8 +31,8 @@ our %S;
scr_size => [ 136, 102 ], # w*h of screenshot thumbnails
ch_size => [ 256, 300 ], # max. w*h of char images
cv_size => [ 256, 400 ], # max. w*h of cover images
- # bit flags (Flag 8 was used for staffedit)
- permissions => {qw| board 1 boardmod 2 edit 4 staffedit 4 tag 16 dbmod 32 tagmod 64 usermod 128 affiliate 256 |},
+ # bit flags (Flag 8 was used for staffedit, now free to re-use)
+ permissions => {qw| board 1 boardmod 2 edit 4 tag 16 dbmod 32 tagmod 64 usermod 128 affiliate 256 |},
default_perm => 1+4+16, # Keep synchronised with the default value of users.perm
default_tags_cat=> 'cont,tech',
languages => [qw|ar ca cs da de en es fi fr he hu id it ja ko nl no pl pt-br pt-pt ro ru sk sv tr uk vi zh|],
diff --git a/data/js/dropdownsearch.js b/data/js/dropdownsearch.js
index 35bd1443..e7c42dc0 100644
--- a/data/js/dropdownsearch.js
+++ b/data/js/dropdownsearch.js
@@ -41,6 +41,7 @@ function box() {
function init(obj, url, trfunc, serfunc, retfunc) {
obj.setAttribute('autocomplete', 'off');
obj.onkeydown = keydown;
+ obj.onclick = obj.onchange = obj.oninput = function() { return textchanged(obj); };
obj.onblur = blur;
obj.ds_returnFunc = retfunc;
obj.ds_trFunc = trfunc;
@@ -48,6 +49,7 @@ function init(obj, url, trfunc, serfunc, retfunc) {
obj.ds_searchURL = url;
obj.ds_selectedId = 0;
obj.ds_dosearch = null;
+ obj.ds_lastVal = obj.value;
}
function blur() {
@@ -65,7 +67,7 @@ function setselected(obj, id) {
function setvalue(obj) {
if(obj.ds_selectedId != 0)
- obj.value = obj.ds_serFunc(byId('ds_box_'+obj.ds_selectedId).ds_itemData, obj);
+ obj.value = obj.ds_lastVal = obj.ds_serFunc(byId('ds_box_'+obj.ds_selectedId).ds_itemData, obj);
if(obj.ds_returnFunc)
obj.ds_returnFunc(obj);
@@ -110,6 +112,21 @@ function updown(obj, up) {
return false;
}
+function textchanged(obj) {
+ // Ignore this event if the text hasn't actually changed.
+ if(obj.ds_lastVal == obj.value)
+ return true;
+ obj.ds_lastVal = obj.value;
+
+ // perform search after a timeout
+ if(obj.ds_dosearch)
+ clearTimeout(obj.ds_dosearch);
+ obj.ds_dosearch = setTimeout(function() {
+ search(obj);
+ }, 500);
+ return true;
+}
+
function keydown(ev) {
var c = document.layers ? ev.which : document.all ? event.keyCode : ev.keyCode;
var obj = this;
@@ -123,14 +140,7 @@ function keydown(ev) {
if(c == 38 || c == 40) // up / down
return updown(obj, c == 38);
- // perform search after a timeout
- if(obj.ds_dosearch)
- clearTimeout(obj.ds_dosearch);
- obj.ds_dosearch = setTimeout(function() {
- search(obj);
- }, 500);
-
- return true;
+ return textchanged(obj);
}
function search(obj) {
diff --git a/data/js/iv.js b/data/js/iv.js
index 75754a90..f1593d22 100644
--- a/data/js/iv.js
+++ b/data/js/iv.js
@@ -1,14 +1,14 @@
/* Simple image viewer widget. Usage:
*
- * <a href="full_image.jpg" rel="iv:{width}x{height}:{category}">..</a>
+ * <a href="full_image.jpg" data-iv="{width}x{height}:{category}">..</a>
*
* Clicking on the above link will cause the image viewer to open
* full_image.jpg. The {category} part can be empty or absent. If it is not
* empty, next/previous links will show up to point to the other images within
* the same category.
*
- * ivInit() should be called when links with "iv:" tags are dynamically added
- * or removed from the DOM.
+ * ivInit() should be called when links with "data-iv" attributes are
+ * dynamically added or removed from the DOM.
*/
// Cache of image categories and the list of associated link objects. Used to
@@ -21,10 +21,10 @@ function init() {
var l = byName('a');
for(var i=0;i<l.length;i++) {
var o = l[i];
- if(o.rel.substr(0,3) == 'iv:' && o.id != 'ivprev' && o.id != 'ivnext') {
+ if(o.getAttribute('data-iv') && o.id != 'ivprev' && o.id != 'ivnext') {
n++;
o.onclick = show;
- var cat = o.rel.split(':')[2];
+ var cat = o.getAttribute('data-iv').split(':')[1];
if(cat) {
if(!cats[cat])
cats[cat] = [];
@@ -60,23 +60,23 @@ function fixnav(lnk, cat, i, dir) {
var a = cat ? findnav(cat, i, dir) : 0;
lnk.style.visibility = a ? 'visible' : 'hidden';
lnk.href = a ? a.href : '#';
- lnk.rel = a ? a.rel : '';
lnk.iv_i = a ? a.iv_i : 0;
+ lnk.setAttribute('data-iv', a ? a.getAttribute('data-iv') : '');
}
function show() {
var u = this.href;
- var opt = this.rel.split(':');
+ var opt = this.getAttribute('data-iv').split(':');
var idx = this.iv_i;
var view = byId('iv_view');
var full = byId('ivfull');
- fixnav(byId('ivprev'), opt[2], idx, -1);
- fixnav(byId('ivnext'), opt[2], idx, 1);
+ fixnav(byId('ivprev'), opt[1], idx, -1);
+ fixnav(byId('ivnext'), opt[1], idx, 1);
// calculate dimensions
- var w = Math.floor(opt[1].split('x')[0]);
- var h = Math.floor(opt[1].split('x')[1]);
+ var w = Math.floor(opt[0].split('x')[0]);
+ var h = Math.floor(opt[0].split('x')[1]);
var ww = typeof(window.innerWidth) == 'number' ? window.innerWidth : document.documentElement.clientWidth;
var wh = typeof(window.innerHeight) == 'number' ? window.innerHeight : document.documentElement.clientHeight;
var st = typeof(window.pageYOffset) == 'number' ? window.pageYOffset : document.body && document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
diff --git a/data/js/lib.js b/data/js/lib.js
index 675f2baa..7d0f6b18 100644
--- a/data/js/lib.js
+++ b/data/js/lib.js
@@ -83,7 +83,7 @@ window.tag = function() {
continue;
if(typeof arguments[i] == 'object' && !arguments[i].appendChild) {
for(attr in arguments[i]) {
- if(attr == 'style')
+ if(attr == 'style' || attr.match(/^data-/))
el.setAttribute(attr, arguments[i][attr]);
else
el[ attr == 'class' ? 'className' : attr == 'for' ? 'htmlFor' : attr ] = arguments[i][attr];
diff --git a/data/js/misc.js b/data/js/misc.js
index 89572e82..f41e8140 100644
--- a/data/js/misc.js
+++ b/data/js/misc.js
@@ -1,18 +1,3 @@
-// search box
-byId('sq').onfocus = function () {
- if(this.value == mt('_menu_emptysearch')) {
- this.value = '';
- this.style.fontStyle = 'normal'
- }
-};
-byId('sq').onblur = function () {
- if(this.value.length < 1) {
- this.value = mt('_menu_emptysearch');
- this.style.fontStyle = 'italic'
- }
-};
-
-
function ulist_redirect(type, path, formcode, args) {
var r = new RegExp('/('+type+'[0-9]+).*$');
location.href = location.href.replace(r, '/$1')+path
@@ -210,7 +195,7 @@ if(byId('batchedit'))
// mouse-over price information / disclaimer
(function(){
if(byId('buynow')) {
- var l = byClass(byId('buynow'), 'acronym', 'pricenote');
+ var l = byClass(byId('buynow'), 'abbr', 'pricenote');
for(var i=0; i<l.length; i++) {
l[i].buynow_last = l[i].title;
l[i].title = null;
@@ -257,12 +242,12 @@ if(byId('not') && byId('vns'))
// Language selector
(function(){
var d = byId('lang_select');
- var flag = byName(d, 'acronym')[0];
+ var flag = byName(d, 'abbr')[0];
ddInit(d, 'bottom', function(lnk) {
var lst = tag('ul', null);
for(var i=0; i<VARS.l10n_lang.length; i++) {
var ln = VARS.l10n_lang[i];
- var icon = tag('acronym', {'class':'icons lang '+ln[0]}, ' ');
+ var icon = tag('abbr', {'class':'icons lang '+ln[0]}, ' ');
lst.appendChild(tag('li', {'class':'lang_selector'}, hasClass(flag, ln[0])
? tag('i', icon, ln[1])
: tag('a', {href:'/setlang?lang='+ln[0]+';ref='+encodeURIComponent(location.pathname+location.search)}, icon, ln[1])
diff --git a/data/js/vnscr.js b/data/js/vnscr.js
index acde013d..08d3ffae 100644
--- a/data/js/vnscr.js
+++ b/data/js/vnscr.js
@@ -98,7 +98,7 @@ function loaded(tr, id, width, height) {
tr.scr_loading = 0;
setContent(byName(tr, 'td')[0],
- tag('a', {href: URL(tr.scr_id, 'f'), rel:'iv:'+dim+':edit'},
+ tag('a', {href: URL(tr.scr_id, 'f'), 'data-iv':dim+':edit'},
tag('img', {src: URL(tr.scr_id, 't')})
)
);
diff --git a/data/lang.txt b/data/lang.txt
index 4827b981..5cb34a3e 100644
--- a/data/lang.txt
+++ b/data/lang.txt
@@ -2839,18 +2839,6 @@ tr : Düzenlemeye karşı kilitli
uk : Заблоковано від редагувань
it : Chiuso alle modifiche
-:_itemmsg_login
-en : You need to be [url,_1,logged in] to edit this page
-ru : Чтобы редактировать эту страницу, вы должны быть [url,_1,авторизованы]
-cs : Pro editaci této stránky musíte být [url,_1,přihlášen]
-hu : Ahhoz, hogy tud szerkeszteni ezt az oldalt be kell legyél [url,_1, jelentkezve].
-nl : Je moet [url,_1,ingelogd] zijn om deze pagina te wijzigen
-de : Du musst [url,_1,eingeloggt sein], um diese Seite zu bearbeiten
-es : Necesitas estar [url,_1,conectado] para editar esta página
-tr : Bu sayfayı düzenlemek için [url,_1,giriş] yapmalısınız.
-uk : Вам необхідно [url,_1,увійти], щоб редагувати цю сторінку
-it : Devi effettuare [url,_1,l'accesso] per modificare questa pagina
-
:_itemmsg_denied
en : You are not allowed to edit this page
ru : Вам запрещено редактировать эту страницу
@@ -16274,6 +16262,18 @@ it : Personaggio principale non valido. Assicurati che ID sia corretto, che il p
:_formerr_e_poll
en : Inappropriate number of options in a poll.
+:_formerr_e_usedalias
+en : Can't remove an alias that is still linked to a VN.
+ru*:
+cs*:
+hu*:
+nl*:
+de*:
+es*:
+tr*:
+uk*:
+it*:
+
:_formerr_title
en : Error
ru : Ошибка
diff --git a/data/style.css b/data/style.css
index 5cc054ef..54f46cb6 100644
--- a/data/style.css
+++ b/data/style.css
@@ -154,9 +154,9 @@ table.formtable tr.newpart td { padding-top: 20px; font-weight: bold; }
#menulist p { text-align: center; }
#menulist input.text { width: 100px; margin-left: 15px; }
#menulist input.submit { width: 90px; margin-left: 20px; }
-#menulist #search input.text { width: 133px; margin: 0 0 3px 7px; font-style: italic; }
+#menulist #search input.text { width: 133px; margin: 0 0 3px 7px }
#menulist #search input.submit { display: none; }
-#dd_box acronym { margin: 2px 5px 2px 0!important; }
+#dd_box abbr { margin: 2px 5px 2px 0!important; }
#menulist .notifyget { display: inline-block; width: 125px; padding: 4px; background: $warnbg$; border: 1px solid $warnborder$; }
@@ -489,7 +489,7 @@ div.scr_uploader { visibility: hidden; overflow: hidden; width: 1px; height:
div.producerbrowse { padding-bottom: 10px }
.producerbrowse ul { float: left; margin-top: -5px; margin-left: 3%; width: 28%; }
.producerbrowse ul li { list-style-type: none; }
-.producerbrowse ul li acronym { margin-right: 5px; margin-top: 1px; }
+.producerbrowse ul li abbr { margin-right: 5px; margin-top: 1px; }
/***** Producer edit *****/
@@ -594,7 +594,7 @@ div.charb td.tc2 b a { color: $grayedout$!important }
div.staffbrowse { padding-bottom: 10px }
.staffbrowse ul { float: left; margin-top: -5px; margin-left: 3%; width: 28%; }
.staffbrowse ul li { list-style-type: none; margin-bottom: 2px; }
-.staffbrowse ul li acronym { margin-right: 5px; margin-top: 1px; }
+.staffbrowse ul li abbr { margin-right: 5px; margin-top: 1px; }
.staffpage table.stripe { width: 400px; margin: 0 auto; }
.staffpage .key { width: 70px; }
.staffroles td.tc2 { white-space: nowrap; width: 80px }
@@ -880,7 +880,7 @@ div#fil_div {
.icons.gen { width: 14px; height: 14px }
.icons.gen.b { width: 28px }
.icons.rtcomplete, .icons.rtpartial, .icons.rttrial { width: 11px; }
-acronym.icons, acronym.uicons { cursor: default; }
+abbr.icons, abbr.uicons { cursor: default; }
a .icons { cursor: pointer }
.icons.oth { background: none; }
$iconcss$
diff --git a/lib/Multi/API.pm b/lib/Multi/API.pm
index d6034160..1ed62682 100644
--- a/lib/Multi/API.pm
+++ b/lib/Multi/API.pm
@@ -11,7 +11,6 @@ use Multi::Core;
use AnyEvent::Socket;
use AnyEvent::Handle;
use POE::Filter::VNDBAPI 'encode_filters';
-use Digest::SHA 'sha256';
use Encode 'encode_utf8', 'decode_utf8';
use Crypt::ScryptKDF 'scrypt_raw';;
use VNDBUtil 'normalize_query', 'norm_ip';
@@ -277,10 +276,7 @@ sub login_verify {
my $uid = $res->value(0,0);
my $accepted = 0;
- if(length $passwd == 41) { # Old sha256
- my $salt = substr $passwd, 0, 9;
- $accepted = sha256($VNDB::S{global_salt}.encode_utf8($arg->{password}).$salt) eq substr $passwd, 9;
- } elsif(length $passwd == 46) { # New scrypt
+ if(length $passwd == 46) { # scrypt
my($N, $r, $p, $salt, $hash) = unpack 'NCCa8a*', $passwd;
$accepted = $hash eq scrypt_raw($arg->{password}, $VNDB::S{scrypt_salt} . $salt, $N, $r, $p, 32);
} else {
@@ -359,8 +355,8 @@ sub splitarray {
# }
# filters => filters args for get_filters() (TODO: Document)
my %GET_VN = (
- sql => 'SELECT %s FROM vn v JOIN vn_rev vr ON v.latest = vr.id WHERE NOT v.hidden AND (%s) %s',
- select => 'v.id, v.latest',
+ sql => 'SELECT %s FROM vn v WHERE NOT v.hidden AND (%s) %s',
+ select => 'v.id',
proc => sub {
delete $_[0]{latest};
$_[0]{id} *= 1
@@ -368,12 +364,12 @@ my %GET_VN = (
sortdef => 'id',
sorts => {
id => 'v.id %s',
- title => 'vr.title %s',
+ title => 'v.title %s',
released => 'v.c_released %s',
},
flags => {
basic => {
- select => 'vr.title, vr.original, v.c_released, v.c_languages, v.c_olang, v.c_platforms',
+ select => 'v.title, v.original, v.c_released, v.c_languages, v.c_olang, v.c_platforms',
proc => sub {
$_[0]{original} ||= undef;
$_[0]{platforms} = splitarray delete $_[0]{c_platforms};
@@ -383,7 +379,7 @@ my %GET_VN = (
},
},
details => {
- select => 'vr.image, vr.img_nsfw, vr.alias AS aliases, vr.length, vr.desc AS description, vr.l_wp, vr.l_encubed, vr.l_renai',
+ select => 'v.image, v.img_nsfw, v.alias AS aliases, v.length, v.desc AS description, v.l_wp, v.l_encubed, v.l_renai',
proc => sub {
$_[0]{aliases} ||= undef;
$_[0]{length} *= 1;
@@ -407,12 +403,12 @@ my %GET_VN = (
},
},
anime => {
- fetch => [[ 'latest', 'SELECT va.vid, a.id, a.year, a.ann_id, a.nfo_id, a.type, a.title_romaji, a.title_kanji
- FROM anime a JOIN vn_anime va ON va.aid = a.id WHERE va.vid IN(%s)',
+ fetch => [[ 'id', 'SELECT va.id AS vid, a.id, a.year, a.ann_id, a.nfo_id, a.type, a.title_romaji, a.title_kanji
+ FROM anime a JOIN vn_anime va ON va.aid = a.id WHERE va.id IN(%s)',
sub { my($r, $n) = @_;
# link
for my $i (@$r) {
- $i->{anime} = [ grep $i->{latest} == $_->{vid}, @$n ];
+ $i->{anime} = [ grep $i->{id} == $_->{vid}, @$n ];
}
# cleanup
for (@$n) {
@@ -425,16 +421,16 @@ my %GET_VN = (
]],
},
relations => {
- fetch => [[ 'latest', 'SELECT vl.vid1, v.id, vl.relation, vr.title, vr.original FROM vn_relations vl
- JOIN vn v ON v.id = vl.vid2 JOIN vn_rev vr ON vr.id = v.latest WHERE vl.vid1 IN(%s) AND NOT v.hidden',
+ fetch => [[ 'id', 'SELECT vr.id AS vid, v.id, vr.relation, v.title, v.original FROM vn_relations vr
+ JOIN vn v ON v.id = vr.vid WHERE vr.id IN(%s)',
sub { my($r, $n) = @_;
for my $i (@$r) {
- $i->{relations} = [ grep $i->{latest} == $_->{vid1}, @$n ];
+ $i->{relations} = [ grep $i->{id} == $_->{vid}, @$n ];
}
for (@$n) {
$_->{id} *= 1;
$_->{original} ||= undef;
- delete $_->{vid1};
+ delete $_->{vid};
}
}
]],
@@ -460,17 +456,17 @@ my %GET_VN = (
[ inta => 'v.id :op:(:value:)', {'=' => 'IN', '!= ' => 'NOT IN'}, range => [1,1e6], join => ',' ],
],
title => [
- [ str => 'vr.title :op: :value:', {qw|= = != <>|} ],
- [ str => 'vr.title ILIKE :value:', {'~',1}, process => \'like' ],
+ [ str => 'v.title :op: :value:', {qw|= = != <>|} ],
+ [ str => 'v.title ILIKE :value:', {'~',1}, process => \'like' ],
],
original => [
- [ undef, "vr.original :op: ''", {qw|= = != <>|} ],
- [ str => 'vr.original :op: :value:', {qw|= = != <>|} ],
- [ str => 'vr.original ILIKE :value:', {'~',1}, process => \'like' ]
+ [ undef, "v.original :op: ''", {qw|= = != <>|} ],
+ [ str => 'v.original :op: :value:', {qw|= = != <>|} ],
+ [ str => 'v.original ILIKE :value:', {'~',1}, process => \'like' ]
],
firstchar => [
- [ undef, '(:op: ((ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)))', {'=', '', '!=', 'NOT'} ],
- [ str => 'LOWER(SUBSTR(vr.title, 1, 1)) :op: :value:' => {qw|= = != <>|}, process => sub { shift =~ /^([a-z])$/ ? $1 : \'Invalid character' } ],
+ [ undef, '(:op: ((ASCII(v.title) < 97 OR ASCII(v.title) > 122) AND (ASCII(v.title) < 65 OR ASCII(v.title) > 90)))', {'=', '', '!=', 'NOT'} ],
+ [ str => 'LOWER(SUBSTR(v.title, 1, 1)) :op: :value:' => {qw|= = != <>|}, process => sub { shift =~ /^([a-z])$/ ? $1 : \'Invalid character' } ],
],
released => [
[ undef, 'v.c_released :op: 0', {qw|= = != <>|} ],
@@ -498,13 +494,13 @@ my %GET_VN = (
);
my %GET_RELEASE = (
- sql => 'SELECT %s FROM releases r JOIN releases_rev rr ON rr.id = r.latest WHERE NOT hidden AND (%s) %s',
- select => 'r.id, r.latest',
+ sql => 'SELECT %s FROM releases r WHERE NOT hidden AND (%s) %s',
+ select => 'r.id',
sortdef => 'id',
sorts => {
id => 'r.id %s',
- title => 'rr.title %s',
- released => 'rr.released %s',
+ title => 'r.title %s',
+ released => 'r.released %s',
},
proc => sub {
delete $_[0]{latest};
@@ -512,7 +508,7 @@ my %GET_RELEASE = (
},
flags => {
basic => {
- select => 'rr.title, rr.original, rr.released, rr.type, rr.patch, rr.freeware, rr.doujin',
+ select => 'r.title, r.original, r.released, r.type, r.patch, r.freeware, r.doujin',
proc => sub {
$_[0]{original} ||= undef;
$_[0]{released} = formatdate($_[0]{released});
@@ -520,16 +516,16 @@ my %GET_RELEASE = (
$_[0]{freeware} = $_[0]{freeware} =~ /^t/ ? TRUE : FALSE;
$_[0]{doujin} = $_[0]{doujin} =~ /^t/ ? TRUE : FALSE;
},
- fetch => [[ 'latest', 'SELECT rid, lang FROM releases_lang WHERE rid IN(%s)',
+ fetch => [[ 'id', 'SELECT id, lang FROM releases_lang WHERE id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{languages} = [ map $i->{latest} == $_->{rid} ? $_->{lang} : (), @$r ];
+ $i->{languages} = [ map $i->{id} == $_->{id} ? $_->{lang} : (), @$r ];
}
},
]],
},
details => {
- select => 'rr.website, rr.notes, rr.minage, rr.gtin, rr.catalog',
+ select => 'r.website, r.notes, r.minage, r.gtin, r.catalog',
proc => sub {
$_[0]{website} ||= undef;
$_[0]{notes} ||= undef;
@@ -538,30 +534,30 @@ my %GET_RELEASE = (
$_[0]{catalog} ||= undef;
},
fetch => [
- [ 'latest', 'SELECT rid, platform FROM releases_platforms WHERE rid IN(%s)',
+ [ 'id', 'SELECT id, platform FROM releases_platforms WHERE id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{platforms} = [ map $i->{latest} == $_->{rid} ? $_->{platform} : (), @$r ];
+ $i->{platforms} = [ map $i->{id} == $_->{id} ? $_->{platform} : (), @$r ];
}
} ],
- [ 'latest', 'SELECT rid, medium, qty FROM releases_media WHERE rid IN(%s)',
+ [ 'id', 'SELECT id, medium, qty FROM releases_media WHERE id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{media} = [ grep $i->{latest} == $_->{rid}, @$r ];
+ $i->{media} = [ grep $i->{id} == $_->{id}, @$r ];
}
for (@$r) {
- delete $_->{rid};
+ delete $_->{id};
$_->{qty} = $VNDB::S{media}{$_->{medium}} ? $_->{qty}*1 : undef;
}
} ],
]
},
vn => {
- fetch => [[ 'latest', 'SELECT rv.rid, v.id, vr.title, vr.original FROM releases_vn rv JOIN vn v ON v.id = rv.vid
- JOIN vn_rev vr ON vr.id = v.latest WHERE NOT v.hidden AND rv.rid IN(%s)',
+ fetch => [[ 'id', 'SELECT rv.id AS rid, v.id, v.title, v.original FROM releases_vn rv JOIN vn v ON v.id = rv.vid
+ WHERE NOT v.hidden AND rv.id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{vn} = [ grep $i->{latest} == $_->{rid}, @$r ];
+ $i->{vn} = [ grep $i->{id} == $_->{rid}, @$r ];
}
for (@$r) {
$_->{id}*=1;
@@ -572,11 +568,11 @@ my %GET_RELEASE = (
]],
},
producers => {
- fetch => [[ 'latest', 'SELECT rp.rid, rp.developer, rp.publisher, p.id, pr.type, pr.name, pr.original FROM releases_producers rp
- JOIN producers p ON p.id = rp.pid JOIN producers_rev pr ON pr.id = p.latest WHERE NOT p.hidden AND rp.rid IN(%s)',
+ fetch => [[ 'id', 'SELECT rp.id AS rid, rp.developer, rp.publisher, p.id, p.type, p.name, p.original FROM releases_producers rp
+ JOIN producers p ON p.id = rp.pid WHERE NOT p.hidden AND rp.id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{producers} = [ grep $i->{latest} == $_->{rid}, @$r ];
+ $i->{producers} = [ grep $i->{id} == $_->{rid}, @$r ];
}
for (@$r) {
$_->{id}*=1;
@@ -595,47 +591,47 @@ my %GET_RELEASE = (
[ inta => 'r.id :op:(:value:)', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', range => [1,1e6] ],
],
vn => [
- [ 'int' => 'rr.id IN(SELECT rv.rid FROM releases_vn rv WHERE rv.vid = :value:)', {'=',1}, range => [1,1e6] ],
+ [ 'int' => 'r.id IN(SELECT rv.id FROM releases_vn rv WHERE rv.vid = :value:)', {'=',1}, range => [1,1e6] ],
],
producer => [
- [ 'int' => 'rr.id IN(SELECT rp.rid FROM releases_producers rp WHERE rp.pid = :value:)', {'=',1}, range => [1,1e6] ],
+ [ 'int' => 'r.id IN(SELECT rp.id FROM releases_producers rp WHERE rp.pid = :value:)', {'=',1}, range => [1,1e6] ],
],
title => [
- [ str => 'rr.title :op: :value:', {qw|= = != <>|} ],
- [ str => 'rr.title ILIKE :value:', {'~',1}, process => \'like' ],
+ [ str => 'r.title :op: :value:', {qw|= = != <>|} ],
+ [ str => 'r.title ILIKE :value:', {'~',1}, process => \'like' ],
],
original => [
- [ undef, "rr.original :op: ''", {qw|= = != <>|} ],
- [ str => 'rr.original :op: :value:', {qw|= = != <>|} ],
- [ str => 'rr.original ILIKE :value:', {'~',1}, process => \'like' ]
+ [ undef, "r.original :op: ''", {qw|= = != <>|} ],
+ [ str => 'r.original :op: :value:', {qw|= = != <>|} ],
+ [ str => 'r.original ILIKE :value:', {'~',1}, process => \'like' ]
],
released => [
- [ undef, 'rr.released :op: 0', {qw|= = != <>|} ],
- [ str => 'rr.released :op: :value:', {qw|= = != <> > > < < <= <= >= >=|}, process => \&parsedate ],
+ [ undef, 'r.released :op: 0', {qw|= = != <>|} ],
+ [ str => 'r.released :op: :value:', {qw|= = != <> > > < < <= <= >= >=|}, process => \&parsedate ],
],
- patch => [ [ bool => 'rr.patch = :value:', {'=',1} ] ],
- freeware => [ [ bool => 'rr.freeware = :value:', {'=',1} ] ],
- doujin => [ [ bool => 'rr.doujin = :value:', {'=',1} ] ],
+ patch => [ [ bool => 'r.patch = :value:', {'=',1} ] ],
+ freeware => [ [ bool => 'r.freeware = :value:', {'=',1} ] ],
+ doujin => [ [ bool => 'r.doujin = :value:', {'=',1} ] ],
type => [
- [ str => 'rr.type :op: :value:', {qw|= = != <>|},
+ [ str => 'r.type :op: :value:', {qw|= = != <>|},
process => sub { !grep($_ eq $_[0], @{$VNDB::S{release_types}}) ? \'No such release type' : $_[0] } ],
],
gtin => [
- [ 'int' => 'rr.gtin :op: :value:', {qw|= = != <>|}, process => sub { length($_[0]) > 14 ? \'Too long GTIN code' : $_[0] } ],
+ [ 'int' => 'r.gtin :op: :value:', {qw|= = != <>|}, process => sub { length($_[0]) > 14 ? \'Too long GTIN code' : $_[0] } ],
],
catalog => [
- [ str => 'rr.catalog :op: :value:', {qw|= = != <>|} ],
+ [ str => 'r.catalog :op: :value:', {qw|= = != <>|} ],
],
languages => [
- [ str => 'rr.id :op:(SELECT rl.rid FROM releases_lang rl WHERE rl.lang = :value:)', {'=' => 'IN', '!=' => 'NOT IN'}, process => \'lang' ],
- [ stra => 'rr.id :op:(SELECT rl.rid FROM releases_lang rl WHERE rl.lang IN(:value:))', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', process => \'lang' ],
+ [ str => 'r.id :op:(SELECT rl.id FROM releases_lang rl WHERE rl.lang = :value:)', {'=' => 'IN', '!=' => 'NOT IN'}, process => \'lang' ],
+ [ stra => 'r.id :op:(SELECT rl.id FROM releases_lang rl WHERE rl.lang IN(:value:))', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', process => \'lang' ],
],
},
);
my %GET_PRODUCER = (
- sql => 'SELECT %s FROM producers p JOIN producers_rev pr ON p.latest = pr.id WHERE NOT p.hidden AND (%s) %s',
- select => 'p.id, p.latest',
+ sql => 'SELECT %s FROM producers p WHERE NOT p.hidden AND (%s) %s',
+ select => 'p.id',
proc => sub {
delete $_[0]{latest};
$_[0]{id} *= 1
@@ -643,17 +639,17 @@ my %GET_PRODUCER = (
sortdef => 'id',
sorts => {
id => 'p.id %s',
- name => 'pr.name %s',
+ name => 'p.name %s',
},
flags => {
basic => {
- select => 'pr.type, pr.name, pr.original, pr.lang AS language',
+ select => 'p.type, p.name, p.original, p.lang AS language',
proc => sub {
$_[0]{original} ||= undef;
},
},
details => {
- select => 'pr.website, pr.l_wp, pr.desc AS description, pr.alias AS aliases',
+ select => 'p.website, p.l_wp, p.desc AS description, p.alias AS aliases',
proc => sub {
$_[0]{description} ||= undef;
$_[0]{aliases} ||= undef;
@@ -664,16 +660,16 @@ my %GET_PRODUCER = (
},
},
relations => {
- fetch => [[ 'latest', 'SELECT pl.pid1, p.id, pl.relation, pr.name, pr.original FROM producers_relations pl
- JOIN producers p ON p.id = pl.pid2 JOIN producers_rev pr ON pr.id = p.latest WHERE pl.pid1 IN(%s) AND NOT p.hidden',
+ fetch => [[ 'id', 'SELECT pl.id AS pid, p.id, pl.relation, p.name, p.original FROM producers_relations pl
+ JOIN producers p ON p.id = pl.pid WHERE pl.id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{relations} = [ grep $i->{latest} == $_->{pid1}, @$r ];
+ $i->{relations} = [ grep $i->{id} == $_->{pid}, @$r ];
}
for (@$r) {
$_->{id}*=1;
$_->{original} ||= undef;
- delete $_->{pid1};
+ delete $_->{pid};
}
},
]],
@@ -685,31 +681,31 @@ my %GET_PRODUCER = (
[ inta => 'p.id :op:(:value:)', {'=' => 'IN', '!= ' => 'NOT IN'}, join => ',', range => [1,1e6] ],
],
name => [
- [ str => 'pr.name :op: :value:', {qw|= = != <>|} ],
- [ str => 'pr.name ILIKE :value:', {'~',1}, process => \'like' ],
+ [ str => 'p.name :op: :value:', {qw|= = != <>|} ],
+ [ str => 'p.name ILIKE :value:', {'~',1}, process => \'like' ],
],
original => [
- [ undef, "pr.original :op: ''", {qw|= = != <>|} ],
- [ str => 'pr.original :op: :value:', {qw|= = != <>|} ],
- [ str => 'pr.original ILIKE :value:', {'~',1}, process => \'like' ]
+ [ undef, "p.original :op: ''", {qw|= = != <>|} ],
+ [ str => 'p.original :op: :value:', {qw|= = != <>|} ],
+ [ str => 'p.original ILIKE :value:', {'~',1}, process => \'like' ]
],
type => [
- [ str => 'pr.type :op: :value:', {qw|= = != <>|},
+ [ str => 'p.type :op: :value:', {qw|= = != <>|},
process => sub { !grep($_ eq $_[0], @{$VNDB::S{producer_types}}) ? \'No such producer type' : $_[0] } ],
],
language => [
- [ str => 'pr.lang :op: :value:', {qw|= = != <>|}, process => \'lang' ],
- [ stra => 'pr.lang :op:(:value:)', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', process => \'lang' ],
+ [ str => 'p.lang :op: :value:', {qw|= = != <>|}, process => \'lang' ],
+ [ stra => 'p.lang :op:(:value:)', {'=' => 'IN', '!=' => 'NOT IN'}, join => ',', process => \'lang' ],
],
search => [
- [ str => '(pr.name ILIKE :value: OR pr.original ILIKE :value: OR pr.alias ILIKE :value:)', {'~',1}, process => \'like' ],
+ [ str => '(p.name ILIKE :value: OR p.original ILIKE :value: OR p.alias ILIKE :value:)', {'~',1}, process => \'like' ],
],
},
);
my %GET_CHARACTER = (
- sql => 'SELECT %s FROM chars c JOIN chars_rev cr ON c.latest = cr.id WHERE NOT c.hidden AND (%s) %s',
- select => 'c.id, c.latest',
+ sql => 'SELECT %s FROM chars c WHERE NOT c.hidden AND (%s) %s',
+ select => 'c.id',
proc => sub {
delete $_[0]{latest};
$_[0]{id} *= 1
@@ -717,11 +713,11 @@ my %GET_CHARACTER = (
sortdef => 'id',
sorts => {
id => 'c.id %s',
- name => 'cr.name %s',
+ name => 'c.name %s',
},
flags => {
basic => {
- select => 'cr.name, cr.original, cr.gender, cr.bloodt, cr.b_day, cr.b_month',
+ select => 'c.name, c.original, c.gender, c.bloodt, c.b_day, c.b_month',
proc => sub {
$_[0]{original} ||= undef;
$_[0]{gender} = undef if $_[0]{gender} eq 'unknown';
@@ -730,7 +726,7 @@ my %GET_CHARACTER = (
},
},
details => {
- select => 'cr.alias AS aliases, cr.image, cr."desc" AS description',
+ select => 'c.alias AS aliases, c.image, c."desc" AS description',
proc => sub {
$_[0]{aliases} ||= undef;
$_[0]{image} = $_[0]{image} ? sprintf '%s/ch/%02d/%d.jpg', $VNDB::S{url_static}, $_[0]{image}%100, $_[0]{image} : undef;
@@ -738,16 +734,16 @@ my %GET_CHARACTER = (
},
},
meas => {
- select => 'cr.s_bust AS bust, cr.s_waist AS waist, cr.s_hip AS hip, cr.height, cr.weight',
+ select => 'c.s_bust AS bust, c.s_waist AS waist, c.s_hip AS hip, c.height, c.weight',
proc => sub {
$_[0]{$_} = $_[0]{$_} ? $_[0]{$_}*1 : undef for(qw|bust waist hip height weight|);
},
},
traits => {
- fetch => [[ 'latest', 'SELECT cid, tid, spoil FROM chars_traits WHERE cid IN(%s)',
+ fetch => [[ 'id', 'SELECT id, tid, spoil FROM chars_traits WHERE id IN(%s)',
sub { my($n, $r) = @_;
for my $i (@$n) {
- $i->{traits} = [ map [ $_->{tid}*1, $_->{spoil}*1 ], grep $i->{latest} == $_->{cid}, @$r ];
+ $i->{traits} = [ map [ $_->{tid}*1, $_->{spoil}*1 ], grep $i->{id} == $_->{id}, @$r ];
}
},
]],
@@ -759,19 +755,19 @@ my %GET_CHARACTER = (
[ inta => 'c.id :op:(:value:)', {'=' => 'IN', '!= ' => 'NOT IN'}, range => [1,1e6], join => ',' ],
],
name => [
- [ str => 'cr.name :op: :value:', {qw|= = != <>|} ],
- [ str => 'cr.name ILIKE :value:', {'~',1}, process => \'like' ],
+ [ str => 'c.name :op: :value:', {qw|= = != <>|} ],
+ [ str => 'c.name ILIKE :value:', {'~',1}, process => \'like' ],
],
original => [
- [ undef, "cr.original :op: ''", {qw|= = != <>|} ],
- [ str => 'cr.original :op: :value:', {qw|= = != <>|} ],
- [ str => 'cr.original ILIKE :value:', {'~',1}, process => \'like' ]
+ [ undef, "c.original :op: ''", {qw|= = != <>|} ],
+ [ str => 'c.original :op: :value:', {qw|= = != <>|} ],
+ [ str => 'c.original ILIKE :value:', {'~',1}, process => \'like' ]
],
search => [
- [ str => '(cr.name ILIKE :value: OR cr.original ILIKE :value: OR cr.alias ILIKE :value:)', {'~',1}, process => \'like' ],
+ [ str => '(c.name ILIKE :value: OR c.original ILIKE :value: OR c.alias ILIKE :value:)', {'~',1}, process => \'like' ],
],
vn => [
- [ 'int' => 'cr.id IN(SELECT cv.cid FROM chars_vns cv WHERE cv.vid = :value:)', {'=',1}, range => [1,1e6] ],
+ [ 'int' => 'c.id IN(SELECT cv.id FROM chars_vns cv WHERE cv.vid = :value:)', {'=',1}, range => [1,1e6] ],
],
},
);
diff --git a/lib/Multi/Anime.pm b/lib/Multi/Anime.pm
index a17eead6..89f74a9e 100644
--- a/lib/Multi/Anime.pm
+++ b/lib/Multi/Anime.pm
@@ -98,10 +98,10 @@ sub unload {
sub check_anime {
- return if $C{aid};
+ return if $C{aid} || $C{tw};
pg_cmd 'SELECT id FROM anime WHERE lastfetch IS NULL OR lastfetch < NOW() - $1::interval ORDER BY lastfetch DESC NULLS FIRST LIMIT 1', [ $O{cachetime} ], sub {
my $res = shift;
- return if pg_expect $res, 1 or $C{aid} or !$res->rows;
+ return if pg_expect $res, 1 or $C{aid} or $C{tw} or !$res->rows;
$C{aid} = $res->value(0,0);
nextcmd();
};
diff --git a/lib/Multi/Feed.pm b/lib/Multi/Feed.pm
index d2161aec..2c4144db 100644
--- a/lib/Multi/Feed.pm
+++ b/lib/Multi/Feed.pm
@@ -44,16 +44,16 @@ sub generate {
# changes
pg_cmd q{
- SELECT '/'||c.type||COALESCE(vr.vid, rr.rid, pr.pid, cr.cid, sr.sid)||'.'||c.rev AS id,
- COALESCE(vr.title, rr.title, pr.name, cr.name, sa.name) AS title, extract('epoch' from c.added) AS updated,
+ SELECT '/'||c.type||COALESCE(v.id, r.id, p.id, c.id, s.id)||'.'||c.rev AS id,
+ COALESCE(v.title, r.title, p.name, ca.name, sa.name) AS title, extract('epoch' from c.added) AS updated,
u.username, u.id AS uid, c.comments AS summary
FROM changes c
- LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id
- LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id
- LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id
- LEFT JOIN chars_rev cr ON c.type = 'c' AND c.id = cr.id
- LEFT JOIN staff_rev sr ON c.type = 's' AND c.id = sr.id
- LEFT JOIN staff_alias sa ON sa.rid = sr.id AND sa.id = sr.aid
+ LEFT JOIN vn v ON c.type = 'v' AND c.itemid = v.id
+ LEFT JOIN releases r ON c.type = 'r' AND c.itemid = r.id
+ LEFT JOIN producers p ON c.type = 'p' AND c.itemid = p.id
+ LEFT JOIN chars ca ON c.type = 'c' AND c.itemid = ca.id
+ LEFT JOIN staff s ON c.type = 's' AND c.itemid = s.id
+ LEFT JOIN staff_alias sa ON sa.id = s.id AND sa.aid = s.aid
JOIN users u ON u.id = c.requester
WHERE c.requester <> 1
ORDER BY c.id DESC
diff --git a/lib/Multi/IRC.pm b/lib/Multi/IRC.pm
index b583e090..6c6159e3 100644
--- a/lib/Multi/IRC.pm
+++ b/lib/Multi/IRC.pm
@@ -18,12 +18,10 @@ use Encode 'decode_utf8', 'encode_utf8';
# long subquery used in several places
my $GETBOARDS = q{array_to_string(array(
- SELECT tb.type||COALESCE(':'||COALESCE(u.username, vr.title, pr.name), '')
+ SELECT tb.type||COALESCE(':'||COALESCE(u.username, v.title, p.name), '')
FROM threads_boards tb
LEFT JOIN vn v ON tb.type = 'v' AND v.id = tb.iid
- LEFT JOIN vn_rev vr ON vr.id = v.latest
LEFT JOIN producers p ON tb.type = 'p' AND p.id = tb.iid
- LEFT JOIN producers_rev pr ON pr.id = p.latest
LEFT JOIN users u ON tb.type = 'u' AND u.id = tb.iid
WHERE tb.tid = t.id
ORDER BY tb.type, tb.iid
@@ -279,24 +277,24 @@ sub handleid {
# plain vn/user/producer/thread/tag/trait/release
pg_cmd 'SELECT $1::text AS type, $2::integer AS id, '.(
- $t eq 'v' ? 'vr.title FROM vn_rev vr JOIN vn v ON v.latest = vr.id WHERE v.id = $2' :
+ $t eq 'v' ? 'v.title FROM vn v WHERE v.id = $2' :
$t eq 'u' ? 'u.username AS title FROM users u WHERE u.id = $2' :
- $t eq 'p' ? 'pr.name AS title FROM producers_rev pr JOIN producers p ON p.latest = pr.id WHERE p.id = $2' :
- $t eq 'c' ? 'cr.name AS title FROM chars_rev cr JOIN chars c ON c.latest = cr.id WHERE c.id = $2' :
- $t eq 's' ? 'sa.name AS title FROM staff_rev sr JOIN staff s ON s.latest = sr.id JOIN staff_alias sa ON sa.id = sr.aid AND sa.rid = s.latest WHERE s.id = $2' :
+ $t eq 'p' ? 'p.name AS title FROM producers p WHERE p.id = $2' :
+ $t eq 'c' ? 'c.name AS title FROM chars c WHERE c.id = $2' :
+ $t eq 's' ? 'sa.name AS title FROM staff s JOIN staff_alias sa ON sa.aid = s.aid AND sa.id = s.id WHERE s.id = $2' :
$t eq 't' ? 'title, '.$GETBOARDS.' FROM threads t WHERE id = $2' :
$t eq 'g' ? 'name AS title FROM tags WHERE id = $2' :
$t eq 'i' ? 'name AS title FROM traits WHERE id = $2' :
- 'rr.title FROM releases_rev rr JOIN releases r ON r.latest = rr.id WHERE r.id = $2'),
+ 'r.title FROM releases r WHERE r.id = $2'),
[ $t, $id ], $c if !$rev && $t =~ /[vprtugics]/;
# edit/insert of vn/release/producer or discussion board post
pg_cmd 'SELECT $1::text AS type, $2::integer AS id, $3::integer AS rev, '.(
- $t eq 'v' ? 'vr.title, u.username, c.comments FROM changes c JOIN vn_rev vr ON c.id = vr.id JOIN users u ON u.id = c.requester WHERE vr.vid = $2 AND c.rev = $3' :
- $t eq 'r' ? 'rr.title, u.username, c.comments FROM changes c JOIN releases_rev rr ON c.id = rr.id JOIN users u ON u.id = c.requester WHERE rr.rid = $2 AND c.rev = $3' :
- $t eq 'p' ? 'pr.name AS title, u.username, c.comments FROM changes c JOIN producers_rev pr ON c.id = pr.id JOIN users u ON u.id = c.requester WHERE pr.pid = $2 AND c.rev = $3' :
- $t eq 'c' ? 'cr.name AS title, u.username, h.comments FROM changes h JOIN chars_rev cr ON h.id = cr.id JOIN users u ON u.id = h.requester WHERE cr.cid = $2 AND h.rev = $3' :
- $t eq 's' ? 'sa.name AS title, u.username, c.comments FROM changes c JOIN staff_rev sr ON c.id = sr.id JOIN users u ON u.id = c.requester JOIN staff_alias sa ON sa.id = sr.aid AND sa.rid = sr.id WHERE sr.sid = $2 AND c.rev = $3' :
+ $t eq 'v' ? 'vh.title, u.username, c.comments FROM changes c JOIN vn_hist vh ON c.id = vh.chid JOIN users u ON u.id = c.requester WHERE c.type = \'v\' AND c.itemid = $2 AND c.rev = $3' :
+ $t eq 'r' ? 'rh.title, u.username, c.comments FROM changes c JOIN releases_hist rh ON c.id = rh.chid JOIN users u ON u.id = c.requester WHERE c.type = \'r\' AND c.itemid = $2 AND c.rev = $3' :
+ $t eq 'p' ? 'ph.name AS title, u.username, c.comments FROM changes c JOIN producers_hist ph ON c.id = ph.chid JOIN users u ON u.id = c.requester WHERE c.type = \'p\' AND c.itemid = $2 AND c.rev = $3' :
+ $t eq 'c' ? 'ch.name AS title, u.username, c.comments FROM changes c JOIN chars_hist ch ON c.id = ch.chid JOIN users u ON u.id = c.requester WHERE c.type = \'c\' AND c.itemid = $2 AND c.rev = $3' :
+ $t eq 's' ? 'sah.name AS title, u.username, c.comments FROM changes c JOIN staff_hist sh ON c.id = sh.chid JOIN users u ON u.id = c.requester JOIN staff_alias_hist sah ON sah.chid = c.id AND sah.aid = sh.aid WHERE c.type = \'s\' AND c.itemid = $2 AND c.rev = $3' :
't.title, u.username, '.$GETBOARDS.' FROM threads t JOIN threads_posts tp ON tp.tid = t.id JOIN users u ON u.id = tp.uid WHERE t.id = $2 AND tp.num = $3'),
[ $t, $id, $rev], $c if $rev && $t =~ /[vprtcs]/;
@@ -341,15 +339,15 @@ sub notify {
my $q = {
rev => q{
- SELECT c.type, c.rev, c.comments, c.id AS lastid,
- COALESCE(vr.vid, rr.rid, pr.pid, cr.cid, sr.sid) AS id, COALESCE(vr.title, rr.title, pr.name, cr.name, sa.name) AS title, u.username
+ SELECT c.type, c.rev, c.comments, c.id AS lastid, c.itemid AS id,
+ COALESCE(vh.title, rh.title, ph.name, ch.name, sah.name) AS title, u.username
FROM changes c
- LEFT JOIN vn_rev vr ON c.type = 'v' AND c.id = vr.id
- LEFT JOIN releases_rev rr ON c.type = 'r' AND c.id = rr.id
- LEFT JOIN producers_rev pr ON c.type = 'p' AND c.id = pr.id
- LEFT JOIN chars_rev cr ON c.type = 'c' AND c.id = cr.id
- LEFT JOIN staff_rev sr ON c.type = 's' AND c.id = sr.id
- LEFT JOIN staff_alias sa ON c.type = 's' AND sa.id = sr.aid AND sa.rid = c.id
+ LEFT JOIN vn_hist vh ON c.type = 'v' AND c.id = vh.chid
+ LEFT JOIN releases_hist rh ON c.type = 'r' AND c.id = rh.chid
+ LEFT JOIN producers_hist ph ON c.type = 'p' AND c.id = ph.chid
+ LEFT JOIN chars_hist ch ON c.type = 'c' AND c.id = ch.chid
+ LEFT JOIN staff_hist sh ON c.type = 's' AND c.id = sh.chid
+ LEFT JOIN staff_alias_hist sah ON c.type = 's' AND sah.aid = sh.aid AND sah.chid = c.id
JOIN users u ON u.id = c.requester
WHERE c.id > $1 AND c.requester <> 1
ORDER BY c.id},
@@ -409,13 +407,12 @@ vn => [ 0, 0, sub {
return $irc->send_msg(PRIVMSG => $chan,
"Couldn't do anything with that search query, you might want to add quotes or use longer words.") if !@q;
- my $w = join ' AND ', map "v.c_search LIKE \$$_", 1..@q;
+ my $w = join ' AND ', map "c_search LIKE \$$_", 1..@q;
pg_cmd qq{
- SELECT 'v'::text AS type, v.id, vr.title
- FROM vn v
- JOIN vn_rev vr ON vr.id = v.latest
- WHERE NOT v.hidden AND $w
- ORDER BY vr.title
+ SELECT 'v'::text AS type, id, title
+ FROM vn
+ WHERE NOT hidden AND $w
+ ORDER BY title
LIMIT 6
}, [ map "%$_%", @q ], sub {
my $res = shift;
@@ -431,11 +428,10 @@ p => [ 0, 0, sub {
my($nick, $chan, $q) = @_;
return $irc->send_msg(PRIVMSG => $chan, 'You forgot the search query, dummy~~!') if !$q;
pg_cmd q{
- SELECT 'p'::text AS type, p.id, pr.name AS title
+ SELECT 'p'::text AS type, id, name AS title
FROM producers p
- JOIN producers_rev pr ON pr.id = p.latest
- WHERE p.hidden = FALSE AND (pr.name ILIKE $1 OR pr.original ILIKE $1 OR pr.alias ILIKE $1)
- ORDER BY pr.name
+ WHERE hidden = FALSE AND (name ILIKE $1 OR original ILIKE $1 OR alias ILIKE $1)
+ ORDER BY name
LIMIT 6
}, [ "%$q%" ], sub {
my $res = shift;
@@ -449,18 +445,17 @@ p => [ 0, 0, sub {
scr => [ 0, 0, sub {
my($nick, $chan, $q) = @_;
- return $irc->send_msg(PRIVMSG => $chan.
+ return $irc->send_msg(PRIVMSG => $chan,
q|Sorry, I failed to comprehend which screenshot you'd like me to lookup for you,|
.q| please understand that Yorhel was not willing to supply me with mind reading capabilities.|)
- if $q !~ /([0-9]+)\.jpg/;
+ if !$q || $q !~ /([0-9]+)\.jpg/;
$q = $1;
pg_cmd q{
- SELECT 'v'::text AS type, v.id, vr.title
- FROM vn v
- JOIN vn_rev vr ON vr.id = v.latest
- JOIN vn_rev vr2 ON vr2.vid = v.id
- JOIN vn_screenshots vs ON vs.vid = vr2.id
- WHERE vs.scr = $1 LIMIT 1
+ SELECT 'v'::text AS type, v.id, v.title
+ FROM changes c
+ JOIN vn_screenshots_hist vsh ON vsh.chid = c.id
+ JOIN vn v ON v.id = c.itemid
+ WHERE vsh.scr = $1 LIMIT 1
}, [ $q ], sub {
my $res = shift;
return if pg_expect $res, 1;
diff --git a/lib/Multi/Maintenance.pm b/lib/Multi/Maintenance.pm
index 23e17fff..dfa708d6 100644
--- a/lib/Multi/Maintenance.pm
+++ b/lib/Multi/Maintenance.pm
@@ -53,17 +53,15 @@ sub log_res {
my %dailies = (
- # takes about 500ms to 5s to complete, depending on how many releases have
- # been released within the past 5 days
+ # takes about 50ms to 500ms to complete, depending on how many releases have been released within the past 5 days
vncache_inc => q|
SELECT update_vncache(id)
FROM (
SELECT DISTINCT rv.vid
FROM releases r
- JOIN releases_rev rr ON rr.id = r.latest
- JOIN releases_vn rv ON rv.rid = r.latest
- WHERE rr.released > TO_CHAR(NOW() - '5 days'::interval, 'YYYYMMDD')::integer
- AND rr.released <= TO_CHAR(NOW(), 'YYYYMMDD')::integer
+ JOIN releases_vn rv ON rv.id = r.id
+ WHERE r.released > TO_CHAR(NOW() - '5 days'::interval, 'YYYYMMDD')::integer
+ AND r.released <= TO_CHAR(NOW(), 'YYYYMMDD')::integer
) AS r(id)|,
# takes about 9 seconds max, still OK
@@ -75,7 +73,7 @@ my %dailies = (
# takes about 30 seconds
vnpopularity => 'SELECT update_vnpopularity()',
- # takes about 25 seconds, can be performed in ranges as well when necessary
+ # takes about 1 second, can be performed in ranges as well when necessary
vnrating => q|
UPDATE vn SET
c_rating = (SELECT (
@@ -126,9 +124,7 @@ sub daily {
my %monthlies = (
- # This takes about 4 to 5 minutes to complete, and should only be necessary
- # in the event that the daily vncache_inc cron hasn't been running for 5
- # subsequent days.
+ # This only takes about 3 seconds to complete
vncache_full => 'SELECT update_vncache(id) FROM vn',
# These shouldn't really be necessary, the triggers in PgSQL should keep
@@ -202,18 +198,9 @@ sub vnsearch_check {
return if pg_expect $res, 1 or !$res->rows;
my $id = $res->value(0,0);
- pg_cmd q|SELECT vr.title, vr.original, vr.alias
- FROM vn v
- JOIN vn_rev vr ON vr.id = v.latest
- WHERE v.id = $1
- UNION
- SELECT rr.title, rr.original, NULL
- FROM releases r
- JOIN releases_rev rr ON rr.id = r.latest
- JOIN releases_vn rv ON rv.rid = r.latest
- WHERE rv.vid = $1
- AND NOT r.hidden
- |, [ $id ], sub { vnsearch_update($id, @_) };
+ pg_cmd q|SELECT title, original, alias FROM vn WHERE id = $1
+ UNION SELECT r.title, r.original, NULL FROM releases r JOIN releases_vn rv ON rv.id = r.id WHERE rv.vid = $1 AND NOT r.hidden|,
+ [ $id ], sub { vnsearch_update($id, @_) };
};
}
diff --git a/lib/Multi/RG.pm b/lib/Multi/RG.pm
index 30101c53..eade569a 100644
--- a/lib/Multi/RG.pm
+++ b/lib/Multi/RG.pm
@@ -40,9 +40,9 @@ sub check_rg {
AE::log debug => 'Checking for new graphs to create.';
pg_cmd q|
- SELECT 'v', v.id FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE rgraph IS NULL AND hidden = FALSE
+ SELECT 'v', v.id FROM vn v JOIN vn_relations vr ON vr.id = v.id WHERE v.rgraph IS NULL AND v.hidden = FALSE
UNION
- SELECT 'p', p.id FROM producers p JOIN producers_relations pr ON pr.pid1 = p.latest WHERE rgraph IS NULL AND hidden = FALSE
+ SELECT 'p', p.id FROM producers p JOIN producers_relations pr ON pr.id = p.id WHERE p.rgraph IS NULL AND p.hidden = FALSE
LIMIT 1|, undef, \&creategraph;
}
@@ -69,8 +69,8 @@ sub getrelid {
my $id = shift;
AE::log debug => "Fetching relations for $C{type}$id";
pg_cmd $C{type} eq 'v'
- ? 'SELECT vid2, relation, official FROM vn v JOIN vn_relations vr ON vr.vid1 = v.latest WHERE v.id = $1'
- : 'SELECT pid2, relation FROM producers p JOIN producers_relations pr ON pr.pid1 = p.latest WHERE p.id = $1',
+ ? 'SELECT vid, relation, official FROM vn_relations WHERE id = $1'
+ : 'SELECT pid, relation FROM producers_relations WHERE id = $1',
[ $id ], sub { getrel($id, @_) };
}
@@ -104,8 +104,8 @@ sub getrel { # id, res, time
my $ids = join(', ', map '$'.$_, 1..@ids);
AE::log debug => "Fetching node information for $C{type}:".join ', ', @ids;
pg_cmd $C{type} eq 'v'
- ? "SELECT v.id, vr.title, v.c_released AS date, array_to_string(v.c_languages, '/') AS lang FROM vn v JOIN vn_rev vr ON vr.id = v.latest WHERE v.id IN($ids) ORDER BY v.c_released"
- : "SELECT p.id, pr.name, pr.lang, pr.type FROM producers p JOIN producers_rev pr ON pr.id = p.latest WHERE p.id IN($ids) ORDER BY pr.name",
+ ? "SELECT id, title, c_released AS date, array_to_string(c_languages, '/') AS lang FROM vn WHERE id IN($ids) ORDER BY c_released"
+ : "SELECT id, name, lang, type FROM producers WHERE id IN($ids) ORDER BY name",
[ @ids ], \&builddot;
}
@@ -256,8 +256,6 @@ sub processgraph {
# Before saving the SVG output, we'll modify it a little:
# - Remove comments
- # - Add svg: prefix to all tags
- # - Remove xmlns declarations (this is set in the html)
# - Remove <title> elements (unused)
# - Remove id attributes (unused)
# - Remove first <polygon> element (emulates the background color)
@@ -274,15 +272,15 @@ sub processgraph {
$attr{class} = 'border' if $attr{stroke} && $attr{stroke} eq '#111111';
$attr{class} = 'nodebg' if $attr{fill} && $attr{fill} eq '#222222';
- delete @attr{qw|stroke fill xmlns xmlns:xlink|};
+ delete @attr{qw|stroke fill|};
delete $attr{id} if $attr{id} && $attr{id} !~ /^node_[vp]\d+$/;
- $w->tag("svg:$el", %attr, $el eq 'path' || $el eq 'polygon' ? undef : ());
+ $w->tag($el, %attr, $el eq 'path' || $el eq 'polygon' ? undef : ());
},
End => sub {
my($expat, $el) = @_;
return if $el eq 'title' || $expat->in_element('title');
return if $el eq 'polygon' && $expat->depth == 2;
- $w->end("svg:$el") if $el ne 'path' && $el ne 'polygon';
+ $w->end($el) if $el ne 'path' && $el ne 'polygon';
},
Char => sub {
my($expat, $str) = @_;
diff --git a/lib/VNDB/DB/Affiliates.pm b/lib/VNDB/DB/Affiliates.pm
index 51f8c2dc..94dfd198 100644
--- a/lib/VNDB/DB/Affiliates.pm
+++ b/lib/VNDB/DB/Affiliates.pm
@@ -23,12 +23,12 @@ sub dbAffiliateGet {
defined($o{hidden}) ? ('!s af.hidden' => $o{hidden} ? '' : 'NOT') : (),
);
- my $join = $o{what} ? 'JOIN releases r ON r.id = af.rid JOIN releases_rev rr ON rr.id = r.latest' : '';
- my $select = $o{what} ? ', rr.title' : '';
+ my $join = $o{what} ? 'JOIN releases r ON r.id = af.rid' : '';
+ my $select = $o{what} ? ', r.title' : '';
my $order = sprintf {
id => 'af.id %s',
- rel => 'rr.title %s',
+ rel => 'r.title %s',
prio => 'af.priority %s',
url => 'af.url %s',
lastfetch => 'af.lastfetch %s',
diff --git a/lib/VNDB/DB/Chars.pm b/lib/VNDB/DB/Chars.pm
index 3db5f869..db9ae93b 100644
--- a/lib/VNDB/DB/Chars.pm
+++ b/lib/VNDB/DB/Chars.pm
@@ -5,10 +5,10 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbCharGet dbCharRevisionInsert dbCharImageId|;
+our @EXPORT = qw|dbCharGet dbCharGetRev dbCharRevisionInsert dbCharImageId|;
-# options: id rev instance tagspoil trait_inc trait_exc char what results page gender bloodt
+# options: id instance tagspoil trait_inc trait_exc char what results page gender bloodt
# bust_min bust_max waist_min waist_max hip_min hip_max height_min height_max weight_min weight_max role
# what: extended traits vns changes
sub dbCharGet {
@@ -24,33 +24,31 @@ sub dbCharGet {
$o{search} =~ s/%//g if $o{search};
my %where = (
- !$o{id} && !$o{rev} ? ( 'c.hidden = FALSE' => 1 ) : (),
- $o{id} ? (
- 'c.id IN(!l)' => [ ref $o{id} ? $o{id} : [$o{id}] ] ) : (),
- $o{rev} ? ( 'h.rev = ?' => $o{rev} ) : (),
+ !$o{id} ? ( 'c.hidden = FALSE' => 1 ) : (),
+ $o{id} ? ( 'c.id IN(!l)' => [ ref $o{id} ? $o{id} : [$o{id}] ] ) : (),
$o{notid} ? ( 'c.id <> ?' => $o{notid} ) : (),
- $o{instance} ? ( 'cr.main = ?' => $o{instance} ) : (),
- $o{vid} ? ( 'cr.id IN(SELECT cid FROM chars_vns WHERE vid = ?)' => $o{vid} ) : (),
- defined $o{gender} ? ( 'cr.gender IN(!l)' => [ ref $o{gender} ? $o{gender} : [$o{gender}] ]) : (),
- defined $o{bloodt} ? ( 'cr.bloodt IN(!l)' => [ ref $o{bloodt} ? $o{bloodt} : [$o{bloodt}] ]) : (),
- defined $o{bust_min} ? ( 'cr.s_bust >= ?' => $o{bust_min} ) : (),
- defined $o{bust_max} ? ( 'cr.s_bust <= ? AND cr.s_bust > 0' => $o{bust_max} ) : (),
- defined $o{waist_min} ? ( 'cr.s_waist >= ?' => $o{waist_min} ) : (),
- defined $o{waist_max} ? ( 'cr.s_waist <= ? AND cr.s_waist > 0' => $o{waist_max} ) : (),
- defined $o{hip_min} ? ( 'cr.s_hip >= ?' => $o{hip_min} ) : (),
- defined $o{hip_max} ? ( 'cr.s_hip <= ? AND cr.s_hip > 0' => $o{hip_max} ) : (),
- defined $o{height_min} ? ( 'cr.height >= ?' => $o{height_min} ) : (),
- defined $o{height_max} ? ( 'cr.height <= ? AND cr.height > 0' => $o{height_max} ) : (),
- defined $o{weight_min} ? ( 'cr.weight >= ?' => $o{weight_min} ) : (),
- defined $o{weight_max} ? ( 'cr.weight <= ? AND cr.weight > 0' => $o{weight_max} ) : (),
+ $o{instance} ? ( 'c.main = ?' => $o{instance} ) : (),
+ $o{vid} ? ( 'c.id IN(SELECT id FROM chars_vns WHERE vid = ?)' => $o{vid} ) : (),
+ defined $o{gender} ? ( 'c.gender IN(!l)' => [ ref $o{gender} ? $o{gender} : [$o{gender}] ]) : (),
+ defined $o{bloodt} ? ( 'c.bloodt IN(!l)' => [ ref $o{bloodt} ? $o{bloodt} : [$o{bloodt}] ]) : (),
+ defined $o{bust_min} ? ( 'c.s_bust >= ?' => $o{bust_min} ) : (),
+ defined $o{bust_max} ? ( 'c.s_bust <= ? AND c.s_bust > 0' => $o{bust_max} ) : (),
+ defined $o{waist_min} ? ( 'c.s_waist >= ?' => $o{waist_min} ) : (),
+ defined $o{waist_max} ? ( 'c.s_waist <= ? AND c.s_waist > 0' => $o{waist_max} ) : (),
+ defined $o{hip_min} ? ( 'c.s_hip >= ?' => $o{hip_min} ) : (),
+ defined $o{hip_max} ? ( 'c.s_hip <= ? AND c.s_hip > 0' => $o{hip_max} ) : (),
+ defined $o{height_min} ? ( 'c.height >= ?' => $o{height_min} ) : (),
+ defined $o{height_max} ? ( 'c.height <= ? AND c.height > 0' => $o{height_max} ) : (),
+ defined $o{weight_min} ? ( 'c.weight >= ?' => $o{weight_min} ) : (),
+ defined $o{weight_max} ? ( 'c.weight <= ? AND c.weight > 0' => $o{weight_max} ) : (),
$o{search} ? (
- '(cr.name ILIKE ? OR cr.original ILIKE ? OR cr.alias ILIKE ?)', [ map '%'.$o{search}.'%', 1..3 ] ) : (),
+ '(c.name ILIKE ? OR c.original ILIKE ? OR c.alias ILIKE ?)', [ map '%'.$o{search}.'%', 1..3 ] ) : (),
$o{char} ? (
- 'LOWER(SUBSTR(cr.name, 1, 1)) = ?' => $o{char} ) : (),
+ 'LOWER(SUBSTR(c.name, 1, 1)) = ?' => $o{char} ) : (),
defined $o{char} && !$o{char} ? (
- '(ASCII(cr.name) < 97 OR ASCII(cr.name) > 122) AND (ASCII(cr.name) < 65 OR ASCII(cr.name) > 90)' => 1 ) : (),
+ '(ASCII(c.name) < 97 OR ASCII(c.name) > 122) AND (ASCII(c.name) < 65 OR ASCII(c.name) > 90)' => 1 ) : (),
$o{role} ? (
- 'EXISTS(SELECT 1 FROM chars_vns cvi WHERE cvi.cid = cr.id AND cvi.role IN(!l))',
+ 'EXISTS(SELECT 1 FROM chars_vns cvi WHERE cvi.id = c.id AND cvi.role IN(!l))',
[ ref $o{role} ? $o{role} : [$o{role}] ] ) : (),
$o{trait_inc} ? (
'c.id IN(SELECT cid FROM traits_chars WHERE tid IN(!l) AND spoil <= ? GROUP BY cid HAVING COUNT(tid) = ?)',
@@ -59,75 +57,103 @@ sub dbCharGet {
'c.id NOT IN(SELECT cid FROM traits_chars WHERE tid IN(!l))' => [ ref $o{trait_exc} ? $o{trait_exc} : [$o{trait_exc}] ] ) : (),
);
- my @select = (qw|c.id cr.name cr.original cr.gender|, 'cr.id AS cid');
- push @select, qw|c.hidden c.locked cr.alias cr.desc cr.image cr.b_month cr.b_day cr.s_bust cr.s_waist cr.s_hip cr.height cr.weight cr.bloodt cr.main cr.main_spoil| if $o{what} =~ /extended/;
- push @select, qw|h.requester h.comments c.latest u.username h.rev h.ihid h.ilock|, "extract('epoch' from h.added) as added" if $o{what} =~ /changes/;
-
- my @join;
- push @join, $o{rev} ? 'JOIN chars c ON c.id = cr.cid' : 'JOIN chars c ON cr.id = c.latest';
- push @join, 'JOIN changes h ON h.id = cr.id' if $o{what} =~ /changes/ || $o{rev};
- push @join, 'JOIN users u ON u.id = h.requester' if $o{what} =~ /changes/;
+ my @select = (qw|c.id c.name c.original c.gender|);
+ push @select, qw|c.hidden c.locked c.alias c.desc c.image c.b_month c.b_day c.s_bust c.s_waist c.s_hip c.height c.weight c.bloodt c.main c.main_spoil| if $o{what} =~ /extended/;
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
- FROM chars_rev cr
- !s
+ FROM chars c
!W
- ORDER BY cr.name|,
- join(', ', @select), join(' ', @join), \%where
+ ORDER BY c.name|,
+ join(', ', @select), \%where
);
- if(@$r && $o{what} =~ /vns|traits|seiyuu/) {
+ return _enrich($self, $r, $np, 0, $o{what}, $o{vid});
+}
+
+
+sub dbCharGetRev {
+ my $self = shift;
+ my %o = (what => '', @_);
+
+ $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'c\' AND itemid = ?', $o{id})->{rev};
+
+ my $select = 'c.itemid AS id, ch.name, ch.original, ch.gender';
+ $select .= ', extract(\'epoch\' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock';
+ $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
+ $select .= ', ch.alias, ch.desc, ch.image, ch.b_month, ch.b_day, ch.s_bust, ch.s_waist, ch.s_hip, ch.height, ch.weight, ch.bloodt, ch.main, ch.main_spoil, co.hidden, co.locked' if $o{what} =~ /extended/;
+
+ my $r = $self->dbAll(q|
+ SELECT !s
+ FROM changes c
+ JOIN chars co ON co.id = c.itemid
+ JOIN chars_hist ch ON ch.chid = c.id
+ JOIN users u ON u.id = c.requester
+ WHERE c.type = 'c' AND c.itemid = ? AND c.rev = ?|,
+ $select, $o{id}, $o{rev}
+ );
+
+ return _enrich($self, $r, 0, 1, $o{what});
+}
+
+
+sub _enrich {
+ my($self, $r, $np, $rev, $what, $vid) = @_;
+
+ if(@$r && $what =~ /vns|traits/) {
+ my($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
my %r = map {
$_->{traits} = [];
$_->{vns} = [];
- $_->{seiyuu} = [];
- ($_->{cid}, $_)
+ ($_->{$col}, $_)
} @$r;
- if($o{what} =~ /traits/) {
- push @{$r{ delete $_->{cid} }{traits}}, $_ for (@{$self->dbAll(q|
- SELECT ct.cid, ct.tid, ct.spoil, t.name, t.sexual, t."group", tg.name AS groupname
- FROM chars_traits ct
+ if($what =~ /traits/) {
+ push @{$r{ delete $_->{xid} }{traits}}, $_ for (@{$self->dbAll(qq|
+ SELECT ct.$colname AS xid, ct.tid, ct.spoil, t.name, t.sexual, t."group", tg.name AS groupname
+ FROM chars_traits$hist ct
JOIN traits t ON t.id = ct.tid
- LEFT JOIN traits tg ON tg.id = t."group"
- WHERE cid IN(!l)
+ JOIN traits tg ON tg.id = t."group"
+ WHERE ct.$colname IN(!l)
ORDER BY tg."order", t.name|, [ keys %r ]
)});
}
- if($o{what} =~ /vns(?:\((\d+)\))?/) {
- push @{$r{ delete $_->{cid} }{vns}}, $_ for (@{$self->dbAll(q|
- SELECT cv.cid, cv.vid, cv.rid, cv.spoil, cv.role, vr.title AS vntitle, rr.title AS rtitle
- FROM chars_vns cv
+ if($what =~ /vns(?:\((\d+)\))?/) {
+ push @{$r{ delete $_->{xid} }{vns}}, $_ for (@{$self->dbAll("
+ SELECT cv.$colname AS xid, cv.vid, cv.rid, cv.spoil, cv.role, v.title AS vntitle, r.title AS rtitle
+ FROM chars_vns$hist cv
JOIN vn v ON cv.vid = v.id
- JOIN vn_rev vr ON vr.id = v.latest
LEFT JOIN releases r ON cv.rid = r.id
- LEFT JOIN releases_rev rr ON rr.id = r.latest
!W
- ORDER BY v.c_released|,
- { 'cv.cid IN(!l)' => [[keys %r]], $1 ? ('cv.vid = ?', $1) : () }
+ ORDER BY v.c_released",
+ { "cv.$colname IN(!l)" => [[keys %r]], $1 ? ('cv.vid = ?', $1) : () }
)});
}
+ }
- if($o{what} =~ /seiyuu/) {
- push @{$r{ delete $_->{cid} }{seiyuu}}, $_ for (@{$self->dbAll(q|
- SELECT cr.id AS cid, s.id AS sid, sa.name, sa.original, vs.note, v.id AS vid, vr.title AS vntitle
- FROM vn_seiyuu vs
- JOIN chars_rev cr ON cr.cid = vs.cid
- JOIN staff_alias sa ON sa.id = vs.aid
- JOIN staff s ON sa.rid = s.latest
- JOIN vn_rev vr ON vr.id = vs.vid
- JOIN vn v ON v.latest = vs.vid
- !W
- ORDER BY v.c_released, sa.name|, {
- 's.hidden = FALSE' => 1,
- 'cr.id IN(!l)' => [[ keys %r ]],
- $o{vid} ? ('v.id = ?' => $o{vid}) : (),
- }
- )});
- }
+ # Depends on the VN revision rather than char revision
+ if(@$r && $what =~ /seiyuu/) {
+ my %r = map {
+ $_->{seiyuu} = [];
+ ($_->{id}, $_)
+ } @$r;
+
+ push @{$r{ delete $_->{cid} }{seiyuu}}, $_ for (@{$self->dbAll(q|
+ SELECT vs.cid, s.id AS sid, sa.name, sa.original, vs.note, v.id AS vid, v.title AS vntitle
+ FROM vn_seiyuu vs
+ JOIN staff_alias sa ON sa.aid = vs.aid
+ JOIN staff s ON s.id = sa.id
+ JOIN vn v ON v.id = vs.id
+ !W
+ ORDER BY v.c_released, sa.name|, {
+ 's.hidden = FALSE' => 1,
+ 'vs.cid IN(!l)' => [[ keys %r ]],
+ $vid ? ('v.id = ?' => $vid) : (),
+ }
+ )});
}
+
return wantarray ? ($r, $np) : $r;
}
@@ -139,15 +165,15 @@ sub dbCharRevisionInsert {
my %set = map exists($o->{$_}) ? (qq|"$_" = ?|, $o->{$_}) : (),
qw|name original alias desc image b_month b_day s_bust s_waist s_hip height weight bloodt gender main main_spoil|;
- $self->dbExec('UPDATE edit_char !H', \%set) if keys %set;
+ $self->dbExec('UPDATE edit_chars !H', \%set) if keys %set;
if($o->{traits}) {
- $self->dbExec('DELETE FROM edit_char_traits');
- $self->dbExec('INSERT INTO edit_char_traits (tid, spoil) VALUES (?,?)', $_->[0],$_->[1]) for (@{$o->{traits}});
+ $self->dbExec('DELETE FROM edit_chars_traits');
+ $self->dbExec('INSERT INTO edit_chars_traits (tid, spoil) VALUES (?,?)', $_->[0],$_->[1]) for (@{$o->{traits}});
}
if($o->{vns}) {
- $self->dbExec('DELETE FROM edit_char_vns');
- $self->dbExec('INSERT INTO edit_char_vns (vid, rid, spoil, role) VALUES(!l)', $_) for (@{$o->{vns}});
+ $self->dbExec('DELETE FROM edit_chars_vns');
+ $self->dbExec('INSERT INTO edit_chars_vns (vid, rid, spoil, role) VALUES(!l)', $_) for (@{$o->{vns}});
}
}
diff --git a/lib/VNDB/DB/Discussions.pm b/lib/VNDB/DB/Discussions.pm
index 2af85c4e..c380feeb 100644
--- a/lib/VNDB/DB/Discussions.pm
+++ b/lib/VNDB/DB/Discussions.pm
@@ -23,11 +23,11 @@ sub dbThreadGet {
!$o{id} ? (
't.hidden = FALSE' => 0 ) : (),
$o{type} && !$o{iid} ? (
- 't.id IN(SELECT tid FROM threads_boards WHERE type IN(!l))' => [ ref $o{type} ? $o{type} : [ $o{type} ] ] ) : (),
+ 'EXISTS(SELECT 1 FROM threads_boards WHERE tid = t.id AND type IN(!l))' => [ ref $o{type} ? $o{type} : [ $o{type} ] ] ) : (),
$o{type} && $o{iid} ? (
'tb.type = ?' => $o{type}, 'tb.iid = ?' => $o{iid} ) : (),
$o{notusers} ? (
- 't.id NOT IN(SELECT tid FROM threads_boards WHERE type = \'u\')' => 1) : (),
+ 'NOT EXISTS(SELECT 1 FROM threads_boards WHERE type = \'u\' AND tid = t.id)' => 1) : (),
);
if($o{search}) {
@@ -39,16 +39,11 @@ sub dbThreadGet {
my @select = (
qw|t.id t.title t.count t.locked t.hidden|,
- $o{what} =~ /firstpost/ ? ('tpf.uid AS fuid', q|EXTRACT('epoch' from tpf.date) AS fdate|, 'uf.username AS fusername') : (),
$o{what} =~ /lastpost/ ? ('tpl.uid AS luid', q|EXTRACT('epoch' from tpl.date) AS ldate|, 'ul.username AS lusername') : (),
'p.id AS poll',
);
my @join = (
- $o{what} =~ /firstpost/ ? (
- 'JOIN threads_posts tpf ON tpf.tid = t.id AND tpf.num = 1',
- 'JOIN users uf ON uf.id = tpf.uid'
- ) : (),
$o{what} =~ /lastpost/ ? (
'JOIN threads_posts tpl ON tpl.tid = t.id AND tpl.num = t.count',
'JOIN users ul ON ul.id = tpl.uid'
@@ -86,14 +81,23 @@ sub dbThreadGet {
[ keys %r ]
)});
}
+
+ if($o{what} =~ /firstpost/) {
+ do { my $x = $r->[$r{$_->{tid}}]; $x->{fuid} = $_->{uid}; $x->{fdate} = $_->{date}; $x->{fusername} = $_->{username} } for (@{$self->dbAll(q|
+ SELECT tpf.tid, tpf.uid, EXTRACT('epoch' from tpf.date) AS date, uf.username
+ FROM threads_posts tpf
+ JOIN users uf ON tpf.uid = uf.id
+ WHERE tpf.num = 1 AND tpf.tid IN(!l)|,
+ [ keys %r ]
+ )});
+ }
+
if($o{what} =~ /boardtitles/) {
push(@{$r->[$r{$_->{tid}}]{boards}}, $_) for (@{$self->dbAll(q|
- SELECT tb.tid, tb.type, tb.iid, COALESCE(u.username, vr.title, pr.name) AS title, COALESCE(u.username, vr.original, pr.original) AS original
+ SELECT tb.tid, tb.type, tb.iid, COALESCE(u.username, v.title, p.name) AS title, COALESCE(u.username, v.original, p.original) AS original
FROM threads_boards tb
LEFT JOIN vn v ON tb.type = 'v' AND v.id = tb.iid
- LEFT JOIN vn_rev vr ON vr.id = v.latest
LEFT JOIN producers p ON tb.type = 'p' AND p.id = tb.iid
- LEFT JOIN producers_rev pr ON pr.id = p.latest
LEFT JOIN users u ON tb.type = 'u' AND u.id = tb.iid
WHERE tb.tid IN(!l)|,
[ keys %r ]
@@ -189,9 +193,9 @@ sub dbPostGet {
$o{hide} && $o{what} =~ /thread/ ? (
't.hidden = FALSE' => 1 ) : (),
$o{search} ? (
- q{to_tsvector('english', strip_bb_tags(msg)) @@ to_tsquery(?)} => $o{search}) : (),
+ 'bb_tsvector(msg) @@ to_tsquery(?)' => $o{search}) : (),
$o{type} ? (
- 'tp.tid = ANY(ARRAY(SELECT tid FROM threads_boards WHERE type IN(!l)))' => [ ref $o{type} ? $o{type} : [ $o{type} ] ] ) : (),
+ 'tp.tid IN(SELECT tid FROM threads_boards WHERE type IN(!l))' => [ ref $o{type} ? $o{type} : [ $o{type} ] ] ) : (),
);
my @select = (
diff --git a/lib/VNDB/DB/Misc.pm b/lib/VNDB/DB/Misc.pm
index 8927e1ec..d6389376 100644
--- a/lib/VNDB/DB/Misc.pm
+++ b/lib/VNDB/DB/Misc.pm
@@ -21,14 +21,13 @@ sub dbStats {
# Inserts a new revision into the database
-# Arguments: type [vrp], revision id, %options->{ editsum uid ihid ilock + db[item]RevisionInsert }
-# revision id = changes.id of the revision this edit is based on, undef to create a new DB item
-# Returns: { iid, cid, rev }
+# Arguments: type [vrp], itemid, rev, %options->{ editsum uid ihid ilock + db[item]RevisionInsert }
+# rev = changes.rev of the revision this edit is based on, undef to create a new DB item
+# Returns: { itemid, chid, rev }
sub dbItemEdit {
- my($self, $type, $oid, %o) = @_;
+ my($self, $type, $itemid, $rev, %o) = @_;
- my $fun = {qw|v vn r release p producer c char s staff|}->{$type};
- $self->dbExec('SELECT edit_!s_init(?)', $fun, $oid);
+ $self->dbExec('SELECT edit_!s_init(?, ?)', $type, $itemid, $rev);
$self->dbExec('UPDATE edit_revision !H', {
'requester = ?' => $o{uid}||$self->authInfo->{id},
'ip = ?' => $self->reqIP,
@@ -43,14 +42,11 @@ sub dbItemEdit {
$self->dbCharRevisionInsert( \%o) if $type eq 'c';
$self->dbStaffRevisionInsert( \%o) if $type eq 's';
- return $self->dbRow('SELECT * FROM edit_!s_commit()', $fun);
+ return $self->dbRow('SELECT * FROM edit_!s_commit()', $type);
}
-# Options: type, iid, uid, auto, hidden, edit, page, results, what, releases
-# what: item user
-# Not very fast in each situation. Can be further optimized by: putting indexes
-# on *_rev.?id, or by caching iid, ititle and ihidden in the changes table.
+# Options: type, itemid, uid, auto, hidden, edit, page, results, releases
sub dbRevisionGet {
my($self, %o) = @_;
$o{results} ||= 10;
@@ -58,68 +54,57 @@ sub dbRevisionGet {
$o{auto} ||= 0; # 0:show, -1:only, 1:hide
$o{hidden} ||= 0;
$o{edit} ||= 0; # 0:both, -1:new, 1:edits
- $o{what} ||= '';
- $o{releases} = 0 if !$o{type} || $o{type} ne 'v' || !$o{iid};
-
- my %tables = qw|v vn r releases p producers c chars s staff|;
- # what types should we join?
- my @types = (
- !$o{type} ? qw(v r p c s) :
- ref($o{type}) ? @{$o{type}} :
- $o{type} ne 'v' ? $o{type} :
- $o{releases} ? ('v', 'r') : 'v'
- );
+ $o{releases} = 0 if !$o{type} || $o{type} ne 'v' || !$o{itemid};
my %where = (
$o{releases} ? (
- q{((h.type = 'v' AND vr.vid = ?) OR (h.type = 'r' AND h.id = ANY(ARRAY(SELECT rv.rid FROM releases_vn rv WHERE rv.vid = ?))))} => [$o{iid}, $o{iid}],
+ # This selects all changes of releases that are currently linked to the VN, not release revisions that are linked to the VN.
+ # The latter seems more useful, but is also a lot more expensive.
+ q{((c.type = 'v' AND c.itemid = ?) OR (c.type = 'r' AND c.itemid = ANY(ARRAY(SELECT rv.id FROM releases_vn rv WHERE rv.vid = ?))))} => [$o{itemid}, $o{itemid}],
) : (
$o{type} ? (
- 'h.type IN(!l)' => [ ref($o{type})?$o{type}:[$o{type}] ] ) : (),
- $o{iid} ? (
- '!sr.!sid = ?' => [ $o{type}, $o{type}, $o{iid} ] ) : (),
+ 'c.type IN(!l)' => [ ref($o{type})?$o{type}:[$o{type}] ] ) : (),
+ $o{itemid} ? (
+ 'c.itemid = ?' => [ $o{itemid} ] ) : (),
),
$o{uid} ? (
- 'h.requester = ?' => $o{uid} ) : (),
+ 'c.requester = ?' => $o{uid} ) : (),
$o{auto} ? (
- 'h.requester !s 1' => $o{auto} < 0 ? '=' : '<>' ) : (),
+ 'c.requester !s 1' => $o{auto} < 0 ? '=' : '<>' ) : (),
$o{hidden} ? (
- '('.join(' OR ', map sprintf('%s.hidden IS NOT NULL AND %s %1$s.hidden', $_, $o{hidden} == 1 ? 'NOT' : ''), @types).')' => 1 ) : (),
+ '!s EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.ihid AND'.
+ ' c2.rev = (SELECT MAX(c3.rev) FROM changes c3 WHERE c3.type = c.type AND c3.itemid = c.itemid))' => $o{hidden} == 1 ? 'NOT' : '') : (),
$o{edit} ? (
- 'h.rev !s 1' => $o{edit} < 0 ? '=' : '>' ) : (),
- );
-
- my @join = (
- $o{iid} || $o{what} =~ /item/ || $o{hidden} || $o{releases} ? (
- map sprintf(q|LEFT JOIN %s_rev %sr ON h.type = '%2$s' AND h.id = %2$sr.id|, $tables{$_}, $_), @types
- ) : (),
- $o{hidden} ? (
- map sprintf(q|LEFT JOIN %s %s ON h.type = '%2$s' AND %2$sr.%2$sid = %2$s.id|, $tables{$_}, $_), @types
- ) : (),
- $o{what} =~ /user/ ? 'JOIN users u ON h.requester = u.id' : (),
- );
- push @join, 'LEFT JOIN staff_alias sa ON sa.rid = sr.id AND sa.id = sr.aid' if grep /s/, @types;
-
- my %tcolumns = qw(v vr.title r rr.title p pr.name c cr.name s sa.name);
- my @select = (
- qw|h.id h.type h.requester h.comments h.rev|,
- q|extract('epoch' from h.added) as added|,
- $o{what} =~ /user/ ? 'u.username' : (),
- $o{what} =~ /item/ ? (
- 'COALESCE('.join(', ', map "${_}r.${_}id", @types).') AS iid',
- 'COALESCE('.join(', ', map $tcolumns{$_}, @types).') AS ititle',
- 'COALESCE('.join(', ', map /s/ ? 'sa.original' : "${_}r.original", @types).') AS ioriginal',
- ) : (),
+ 'c.rev !s 1' => $o{edit} < 0 ? '=' : '>' ) : (),
);
my($r, $np) = $self->dbPage(\%o, q|
- SELECT !s
- FROM changes h
- !s
+ SELECT c.id, c.type, c.itemid, c.requester, c.comments, c.rev, extract('epoch' from c.added) as added, u.username
+ FROM changes c
+ JOIN users u ON c.requester = u.id
!W
- ORDER BY h.id DESC|,
- join(', ', @select), join(' ', @join), \%where
+ ORDER BY c.id DESC|, \%where
);
+
+ # I couldn't find a way to fetch the titles the main query above without slowing it down considerably, so let's just do it this way.
+ if(@$r) {
+ my %r = map +($_->{id}, $_), @$r;
+ my $w = join ' OR ', ('(type = ? AND id = ?)') x @$r;
+ my @w = map +($_->{type}, $_->{id}), @$r;
+
+ $r{ $_->{id} }{ititle} = $_->{title}, $r{ $_->{id} }{ioriginal} = $_->{original} for(@{$self->dbAll("
+ SELECT id, title, original FROM (
+ SELECT 'v'::dbentry_type, chid, title, original FROM vn_hist
+ UNION ALL SELECT 'r'::dbentry_type, chid, title, original FROM releases_hist
+ UNION ALL SELECT 'p'::dbentry_type, chid, name, original FROM producers_hist
+ UNION ALL SELECT 'c'::dbentry_type, chid, name, original FROM chars_hist
+ UNION ALL SELECT 's'::dbentry_type, sh.chid, name, original FROM staff_hist sh JOIN staff_alias_hist sah ON sah.chid = sh.chid AND sah.aid = sh.aid
+ ) x(type, id, title, original)
+ WHERE $w
+ ", @w
+ )});
+ }
+
return wantarray ? ($r, $np) : $r;
}
diff --git a/lib/VNDB/DB/Producers.pm b/lib/VNDB/DB/Producers.pm
index 3ff70ae1..3b6f2a32 100644
--- a/lib/VNDB/DB/Producers.pm
+++ b/lib/VNDB/DB/Producers.pm
@@ -4,12 +4,13 @@ package VNDB::DB::Producers;
use strict;
use warnings;
use Exporter 'import';
+use Encode 'decode_utf8';
-our @EXPORT = qw|dbProducerGet dbProducerRevisionInsert|;
+our @EXPORT = qw|dbProducerGet dbProducerGetRev dbProducerRevisionInsert|;
-# options: results, page, id, search, char, rev
-# what: extended changes relations relgraph
+# options: results, page, id, search, char
+# what: extended relations relgraph
sub dbProducerGet {
my $self = shift;
my %o = (
@@ -22,52 +23,80 @@ sub dbProducerGet {
$o{search} =~ s/%//g if $o{search};
my %where = (
- !$o{id} && !$o{rev} ? (
+ !$o{id} ? (
'p.hidden = FALSE' => 1 ) : (),
$o{id} ? (
'p.id IN(!l)' => [ ref $o{id} ? $o{id} : [$o{id}] ] ) : (),
$o{search} ? (
- '(pr.name ILIKE ? OR pr.original ILIKE ? OR pr.alias ILIKE ?)', [ map '%'.$o{search}.'%', 1..3 ] ) : (),
+ '(p.name ILIKE ? OR p.original ILIKE ? OR p.alias ILIKE ?)', [ map '%'.$o{search}.'%', 1..3 ] ) : (),
$o{char} ? (
- 'LOWER(SUBSTR(pr.name, 1, 1)) = ?' => $o{char} ) : (),
+ 'LOWER(SUBSTR(p.name, 1, 1)) = ?' => $o{char} ) : (),
defined $o{char} && !$o{char} ? (
- '(ASCII(pr.name) < 97 OR ASCII(pr.name) > 122) AND (ASCII(pr.name) < 65 OR ASCII(pr.name) > 90)' => 1 ) : (),
- $o{rev} ? (
- 'c.rev = ?' => $o{rev} ) : (),
+ '(ASCII(p.name) < 97 OR ASCII(p.name) > 122) AND (ASCII(p.name) < 65 OR ASCII(p.name) > 90)' => 1 ) : (),
);
- my @join;
- push @join, $o{rev} ? 'JOIN producers p ON p.id = pr.pid' : 'JOIN producers p ON pr.id = p.latest';
- push @join, 'JOIN changes c ON c.id = pr.id' if $o{what} =~ /changes/ || $o{rev};
- push @join, 'JOIN users u ON u.id = c.requester' if $o{what} =~ /changes/;
- push @join, 'JOIN relgraphs pg ON pg.id = p.rgraph' if $o{what} =~ /relgraph/;
+ my $join = $o{what} =~ /relgraph/ ? 'JOIN relgraphs pg ON pg.id = p.rgraph' : '';
- my $select = 'p.id, pr.type, pr.name, pr.original, pr.lang, pr.id AS cid, p.rgraph';
- $select .= ', pr.desc, pr.alias, pr.website, pr.l_wp, p.hidden, p.locked' if $o{what} =~ /extended/;
- $select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, p.latest, pr.id AS cid, u.username, c.rev, c.ihid, c.ilock| if $o{what} =~ /changes/;
+ my $select = 'p.id, p.type, p.name, p.original, p.lang, p.rgraph';
+ $select .= ', p.desc, p.alias, p.website, p.l_wp, p.hidden, p.locked' if $o{what} =~ /extended/;
$select .= ', pg.svg' if $o{what} =~ /relgraph/;
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
- FROM producers_rev pr
+ FROM producers p
!s
!W
- ORDER BY pr.name ASC|,
- $select, join(' ', @join), \%where,
+ ORDER BY p.name ASC|,
+ $select, $join, \%where,
);
- if(@$r && $o{what} =~ /relations/) {
+ $_->{svg} && ($_->{svg} = decode_utf8($_->{svg})) for (@$r);
+ return _enrich($self, $r, $np, 0, $o{what});
+}
+
+
+# options: id, rev, what
+# what: extended relations
+sub dbProducerGetRev {
+ my $self = shift;
+ my %o = (what => '', @_);
+
+ $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'p\' AND itemid = ?', $o{id})->{rev};
+
+ my $select = 'c.itemid AS id, p.type, p.name, p.original, p.lang, po.rgraph';
+ $select .= ', extract(\'epoch\' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock';
+ $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
+ $select .= ', p.desc, p.alias, p.website, p.l_wp, po.hidden, po.locked' if $o{what} =~ /extended/;
+
+ my $r = $self->dbAll(q|
+ SELECT !s
+ FROM changes c
+ JOIN producers po ON po.id = c.itemid
+ JOIN producers_hist p ON p.chid = c.id
+ JOIN users u ON u.id = c.requester
+ WHERE c.type = 'p' AND c.itemid = ? AND c.rev = ?|,
+ $select, $o{id}, $o{rev}
+ );
+
+ return _enrich($self, $r, 0, 1, $o{what});
+}
+
+
+sub _enrich {
+ my($self, $r, $np, $rev, $what) = @_;
+
+ if(@$r && $what =~ /relations/) {
+ my($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
my %r = map {
$r->[$_]{relations} = [];
- ($r->[$_]{cid}, $_)
+ ($r->[$_]{$col}, $_)
} 0..$#$r;
- push @{$r->[$r{$_->{pid1}}]{relations}}, $_ for(@{$self->dbAll(q|
- SELECT rel.pid1, rel.pid2 AS id, rel.relation, pr.name, pr.original
- FROM producers_relations rel
- JOIN producers p ON rel.pid2 = p.id
- JOIN producers_rev pr ON p.latest = pr.id
- WHERE rel.pid1 IN(!l)|,
+ push @{$r->[$r{$_->{xid}}]{relations}}, $_ for(@{$self->dbAll(qq|
+ SELECT rel.$colname AS xid, rel.pid AS id, rel.relation, p.name, p.original
+ FROM producers_relations$hist rel
+ JOIN producers p ON rel.pid = p.id
+ WHERE rel.$colname IN(!l)|,
[ keys %r ]
)});
}
@@ -83,13 +112,13 @@ sub dbProducerRevisionInsert {
my %set = map exists($o->{$_}) ? (qq|"$_" = ?|, $o->{$_}) : (),
qw|name original website l_wp type lang desc alias|;
- $self->dbExec('UPDATE edit_producer !H', \%set) if keys %set;
+ $self->dbExec('UPDATE edit_producers !H', \%set) if keys %set;
if($o->{relations}) {
- $self->dbExec('DELETE FROM edit_producer_relations');
+ $self->dbExec('DELETE FROM edit_producers_relations');
my $q = join ',', map '(?,?)', @{$o->{relations}};
my @q = map +($_->[1], $_->[0]), @{$o->{relations}};
- $self->dbExec("INSERT INTO edit_producer_relations (pid, relation) VALUES $q", @q) if @q;
+ $self->dbExec("INSERT INTO edit_producers_relations (pid, relation) VALUES $q", @q) if @q;
}
}
diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm
index 3c19cfb2..eafb84c0 100644
--- a/lib/VNDB/DB/Releases.pm
+++ b/lib/VNDB/DB/Releases.pm
@@ -7,12 +7,12 @@ use POSIX 'strftime';
use Exporter 'import';
use VNDB::Func 'gtintype';
-our @EXPORT = qw|dbReleaseGet dbReleaseRevisionInsert|;
+our @EXPORT = qw|dbReleaseGet dbReleaseGetRev dbReleaseRevisionInsert|;
-# Options: id vid pid rev released page results what med sort reverse date_before date_after
+# Options: id vid pid released page results what med sort reverse date_before date_after
# plat lang olang type minage search resolution freeware doujin voiced ani_story ani_ero
-# What: extended changes vn producers platforms media affiliates
+# What: extended vn producers platforms media affiliates
# Sort: title released minage
sub dbReleaseGet {
my($self, %o) = @_;
@@ -23,34 +23,33 @@ sub dbReleaseGet {
$o{med} = [ $o{med} ] if $o{med} && !ref $o{med};
my @where = (
- !$o{id} && !$o{rev} ? ( 'r.hidden = FALSE' => 0 ) : (),
+ !$o{id} ? ( 'r.hidden = FALSE' => 0 ) : (),
$o{id} ? ( 'r.id = ?' => $o{id} ) : (),
- $o{rev} ? ( 'c.rev = ?' => $o{rev} ) : (),
$o{vid} ? ( 'rv.vid IN(!l)' => [ ref $o{vid} ? $o{vid} : [$o{vid}] ] ) : (),
$o{pid} ? ( 'rp.pid = ?' => $o{pid} ) : (),
- defined $o{patch} ? ( 'rr.patch = ?' => $o{patch} == 1 ? 1 : 0) : (),
- defined $o{freeware} ? ( 'rr.freeware = ?' => $o{freeware} == 1 ? 1 : 0) : (),
- defined $o{doujin} ? ( 'rr.doujin = ?' => $o{doujin} == 1 ? 1 : 0) : (),
- defined $o{type} ? ( 'rr.type = ?' => $o{type} ) : (),
- defined $o{date_before} ? ( 'rr.released <= ?' => $o{date_before} ) : (),
- defined $o{date_after} ? ( 'rr.released >= ?' => $o{date_after} ) : (),
- defined $o{minage} ? ( 'rr.minage IN(!l)' => [ ref $o{minage} ? $o{minage} : [$o{minage}] ] ) : (),
- defined $o{resolution} ? ( 'rr.resolution IN(!l)' => [ ref $o{resolution} ? $o{resolution} : [$o{resolution}] ] ) : (),
- defined $o{voiced} ? ( 'rr.voiced IN(!l)' => [ ref $o{voiced} ? $o{voiced} : [$o{voiced}] ] ) : (),
- defined $o{ani_story} ? ( 'rr.ani_story IN(!l)' => [ ref $o{ani_story} ? $o{ani_story} : [$o{ani_story}] ] ) : (),
- defined $o{ani_ero} ? ( 'rr.ani_ero IN(!l)' => [ ref $o{ani_ero} ? $o{ani_ero} : [$o{ani_ero}] ] ) : (),
- defined $o{released} ? ( 'rr.released !s ?' => [ $o{released} ? '<=' : '>', strftime('%Y%m%d', gmtime) ] ) : (),
+ defined $o{patch} ? ( 'r.patch = ?' => $o{patch} == 1 ? 1 : 0) : (),
+ defined $o{freeware} ? ( 'r.freeware = ?' => $o{freeware} == 1 ? 1 : 0) : (),
+ defined $o{doujin} ? ( 'r.doujin = ?' => $o{doujin} == 1 ? 1 : 0) : (),
+ defined $o{type} ? ( 'r.type = ?' => $o{type} ) : (),
+ defined $o{date_before} ? ( 'r.released <= ?' => $o{date_before} ) : (),
+ defined $o{date_after} ? ( 'r.released >= ?' => $o{date_after} ) : (),
+ defined $o{minage} ? ( 'r.minage IN(!l)' => [ ref $o{minage} ? $o{minage} : [$o{minage}] ] ) : (),
+ defined $o{resolution} ? ( 'r.resolution IN(!l)' => [ ref $o{resolution} ? $o{resolution} : [$o{resolution}] ] ) : (),
+ defined $o{voiced} ? ( 'r.voiced IN(!l)' => [ ref $o{voiced} ? $o{voiced} : [$o{voiced}] ] ) : (),
+ defined $o{ani_story} ? ( 'r.ani_story IN(!l)' => [ ref $o{ani_story} ? $o{ani_story} : [$o{ani_story}] ] ) : (),
+ defined $o{ani_ero} ? ( 'r.ani_ero IN(!l)' => [ ref $o{ani_ero} ? $o{ani_ero} : [$o{ani_ero}] ] ) : (),
+ defined $o{released} ? ( 'r.released !s ?' => [ $o{released} ? '<=' : '>', strftime('%Y%m%d', gmtime) ] ) : (),
$o{lang} ? (
- 'rr.id IN(SELECT irl.rid FROM releases_lang irl JOIN releases ir ON ir.latest = irl.rid WHERE irl.lang IN(!l))' => [ ref $o{lang} ? $o{lang} : [ $o{lang} ] ] ) : (),
+ 'r.id IN(SELECT irl.id FROM releases_lang irl WHERE irl.lang IN(!l))' => [ ref $o{lang} ? $o{lang} : [ $o{lang} ] ] ) : (),
$o{olang} ? (
- 'rr.id IN(SELECT irv.rid FROM releases_vn irv JOIN releases ir ON ir.latest = irv.rid JOIN vn v ON irv.vid = v.id WHERE v.c_olang && ARRAY[!l]::language[])' => [ ref $o{olang} ? $o{olang} : [ $o{olang} ] ] ) : (),
+ 'r.id IN(SELECT irv.id FROM releases_vn irv JOIN vn v ON irv.vid = v.id WHERE v.c_olang && ARRAY[!l]::language[])' => [ ref $o{olang} ? $o{olang} : [ $o{olang} ] ] ) : (),
$o{plat} ? ('('.join(' OR ',
- grep(/^unk$/, @{$o{plat}}) ? 'NOT EXISTS(SELECT 1 FROM releases_platforms irp WHERE irp.rid = r.latest)' : (),
- grep(!/^unk$/, @{$o{plat}}) ? 'rr.id IN(SELECT irp.rid FROM releases_platforms irp JOIN releases ir ON ir.latest = irp.rid WHERE irp.platform IN(!l))' : (),
+ grep(/^unk$/, @{$o{plat}}) ? 'NOT EXISTS(SELECT 1 FROM releases_platforms irp WHERE irp.id = r.id)' : (),
+ grep(!/^unk$/, @{$o{plat}}) ? 'r.id IN(SELECT irp.id FROM releases_platforms irp WHERE irp.platform IN(!l))' : (),
).')', [ [ grep !/^unk$/, @{$o{plat}} ] ]) : (),
$o{med} ? ('('.join(' OR ',
- grep(/^unk$/, @{$o{med}}) ? 'NOT EXISTS(SELECT 1 FROM releases_media irm WHERE irm.rid = r.latest)' : (),
- grep(!/^unk$/, @{$o{med}}) ? 'rr.id IN(SELECT irm.rid FROM releases_media irm JOIN releases ir ON ir.latest = irm.rid WHERE irm.medium IN(!l))' : ()
+ grep(/^unk$/, @{$o{med}}) ? 'NOT EXISTS(SELECT 1 FROM releases_media irm WHERE irm.id = r.id)' : (),
+ grep(!/^unk$/, @{$o{med}}) ? 'r.id IN(SELECT irm.id FROM releases_media irm WHERE irm.medium IN(!l))' : ()
).')', [ [ grep(!/^unk$/, @{$o{med}}) ] ]) : (),
);
@@ -58,110 +57,135 @@ sub dbReleaseGet {
for (split /[ -,._]/, $o{search}) {
s/%//g;
if(/^\d+$/ && gtintype($_)) {
- push @where, 'rr.gtin = ?', $_;
+ push @where, 'r.gtin = ?', $_;
} elsif(length($_) > 0) {
$_ = "%$_%";
- push @where, '(rr.title ILIKE ? OR rr.original ILIKE ? OR rr.catalog = ?)',
+ push @where, '(r.title ILIKE ? OR r.original ILIKE ? OR r.catalog = ?)',
[ $_, $_, $_ ];
}
}
}
my @join = (
- $o{rev} ? 'JOIN releases r ON r.id = rr.rid' : 'JOIN releases r ON rr.id = r.latest',
- $o{vid} ? 'JOIN releases_vn rv ON rv.rid = rr.id' : (),
- $o{pid} ? 'JOIN releases_producers rp ON rp.rid = rr.id' : (),
- $o{what} =~ /changes/ || $o{rev} ? (
- 'JOIN changes c ON c.id = rr.id',
- 'JOIN users u ON u.id = c.requester'
- ) : (),
+ $o{vid} ? 'JOIN releases_vn rv ON rv.id = r.id' : (),
+ $o{pid} ? 'JOIN releases_producers rp ON rp.id = r.id' : (),
);
my @select = (
- qw|r.id rr.title rr.original rr.website rr.released rr.minage rr.type rr.patch|,
- 'rr.id AS cid',
- $o{what} =~ /extended/ ? qw|rr.notes rr.catalog rr.gtin rr.resolution rr.voiced rr.freeware rr.doujin rr.ani_story rr.ani_ero r.hidden r.locked| : (),
- $o{what} =~ /changes/ ?
- (qw|c.requester c.comments r.latest u.username c.rev c.ihid c.ilock|, q|extract('epoch' from c.added) as added|) : (),
+ qw|r.id r.title r.original r.website r.released r.minage r.type r.patch|,
+ $o{what} =~ /extended/ ? qw|r.notes r.catalog r.gtin r.resolution r.voiced r.freeware r.doujin r.ani_story r.ani_ero r.hidden r.locked| : (),
$o{pid} ? ('rp.developer', 'rp.publisher') : (),
);
my $order = sprintf {
- title => 'rr.title %s, rr.released %1$s',
- type => 'rr.patch %s, rr.type %1$s, rr.released %1$s, rr.title %1$s',
- publication => 'rr.doujin %s, rr.freeware %1$s, rr.patch %1$s, rr.released %1$s, rr.title %1$s',
- resolution => 'rr.resolution %s, rr.patch %2$s, rr.released %1$s, rr.title %1$s',
- voiced => 'rr.voiced %s, rr.patch %2$s, rr.released %1$s, rr.title %1$s',
- ani_ero => 'rr.ani_story %s, rr.ani_ero %1$s, rr.patch %2$s, rr.released %1$s, rr.title %1$s',
- released => 'rr.released %s, r.id %1$s',
- minage => 'rr.minage %s, rr.released %1$s, rr.title %1$s',
- notes => 'rr.notes %s, rr.released %1$s, rr.title %1$s',
+ title => 'r.title %s, r.released %1$s',
+ type => 'r.patch %s, r.type %1$s, r.released %1$s, r.title %1$s',
+ publication => 'r.doujin %s, r.freeware %1$s, r.patch %1$s, r.released %1$s, r.title %1$s',
+ resolution => 'r.resolution %s, r.patch %2$s, r.released %1$s, r.title %1$s',
+ voiced => 'r.voiced %s, r.patch %2$s, r.released %1$s, r.title %1$s',
+ ani_ero => 'r.ani_story %s, r.ani_ero %1$s, r.patch %2$s, r.released %1$s, r.title %1$s',
+ released => 'r.released %s, r.id %1$s',
+ minage => 'r.minage %s, r.released %1$s, r.title %1$s',
+ notes => 'r.notes %s, r.released %1$s, r.title %1$s',
}->{ $o{sort}||'released' }, $o{reverse} ? 'DESC' : 'ASC', !$o{reverse} ? 'DESC' : 'ASC';
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
- FROM releases_rev rr
+ FROM releases r
!s
!W
ORDER BY !s|,
join(', ', @select), join(' ', @join), \@where, $order
);
+ return _enrich($self, $r, $np, 0, $o{what});
+}
+
+
+# options: id, rev, what
+# what: extended vn producers platforms media affiliates
+sub dbReleaseGetRev {
+ my $self = shift;
+ my %o = (what => '', @_);
+
+ $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'r\' AND itemid = ?', $o{id})->{rev};
+
+ my $select = 'c.itemid AS id, r.title, r.original, r.website, r.released, r.minage, r.type, r.patch';
+ $select .= ', r.notes, r.catalog, r.gtin, r.resolution, r.voiced, r.freeware, r.doujin, r.ani_story, r.ani_ero, ro.hidden, ro.locked' if $o{what} =~ /extended/;
+ $select .= ', extract(\'epoch\' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock';
+ $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
+
+ my $r = $self->dbAll(q|
+ SELECT !s
+ FROM changes c
+ JOIN releases ro ON ro.id = c.itemid
+ JOIN releases_hist r ON r.chid = c.id
+ JOIN users u ON u.id = c.requester
+ WHERE c.type = 'r' AND c.itemid = ? AND c.rev = ?|,
+ $select, $o{id}, $o{rev}
+ );
+
+ return _enrich($self, $r, 0, 1, $o{what});
+}
+
+
+sub _enrich {
+ my($self, $r, $np, $rev, $what) = @_;
+
if(@$r) {
+ my($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
my %r = map {
$r->[$_]{producers} = [];
$r->[$_]{platforms} = [];
$r->[$_]{media} = [];
$r->[$_]{vn} = [];
$r->[$_]{languages} = [];
- ($r->[$_]{cid}, $_)
+ ($r->[$_]{$col}, $_)
} 0..$#$r;
- push(@{$r->[$r{$_->{rid}}]{languages}}, $_->{lang}) for (@{$self->dbAll(q|
- SELECT rid, lang
- FROM releases_lang
- WHERE rid IN(!l)|,
+ push(@{$r->[$r{$_->{xid}}]{languages}}, $_->{lang}) for (@{$self->dbAll("
+ SELECT $colname AS xid, lang
+ FROM releases_lang$hist
+ WHERE $colname IN(!l)",
[ keys %r ]
)});
- if($o{what} =~ /vn/) {
- push(@{$r->[$r{$_->{rid}}]{vn}}, $_) for (@{$self->dbAll(q|
- SELECT rv.rid, vr.vid, vr.title, vr.original
- FROM releases_vn rv
+ if($what =~ /vn/) {
+ push(@{$r->[$r{$_->{xid}}]{vn}}, $_) for (@{$self->dbAll("
+ SELECT rv.$colname AS xid, v.id AS vid, v.title, v.original
+ FROM releases_vn$hist rv
JOIN vn v ON v.id = rv.vid
- JOIN vn_rev vr ON vr.id = v.latest
- WHERE rv.rid IN(!l)
- ORDER BY vr.title|,
+ WHERE rv.$colname IN(!l)
+ ORDER BY v.title",
[ keys %r ]
)});
}
- if($o{what} =~ /producers/) {
- push(@{$r->[$r{$_->{rid}}]{producers}}, $_) for (@{$self->dbAll(q|
- SELECT rp.rid, rp.developer, rp.publisher, p.id, pr.name, pr.original, pr.type
- FROM releases_producers rp
+ if($what =~ /producers/) {
+ push(@{$r->[$r{$_->{xid}}]{producers}}, $_) for (@{$self->dbAll("
+ SELECT rp.$colname AS xid, rp.developer, rp.publisher, p.id, p.name, p.original, p.type
+ FROM releases_producers$hist rp
JOIN producers p ON rp.pid = p.id
- JOIN producers_rev pr ON pr.id = p.latest
- WHERE rp.rid IN(!l)
- ORDER BY pr.name|,
+ WHERE rp.$colname IN(!l)
+ ORDER BY p.name",
[ keys %r ]
)});
}
- if($o{what} =~ /platforms/) {
- push(@{$r->[$r{$_->{rid}}]{platforms}}, $_->{platform}) for (@{$self->dbAll(q|
- SELECT rid, platform
- FROM releases_platforms
- WHERE rid IN(!l)|,
+ if($what =~ /platforms/) {
+ push(@{$r->[$r{$_->{xid}}]{platforms}}, $_->{platform}) for (@{$self->dbAll("
+ SELECT $colname AS xid, platform
+ FROM releases_platforms$hist
+ WHERE $colname IN(!l)",
[ keys %r ]
)});
}
- if($o{what} =~ /media/) {
- push(@{$r->[$r{$_->{rid}}]{media}}, $_) for (@{$self->dbAll(q|
- SELECT rid, medium, qty
- FROM releases_media
- WHERE rid IN(!l)|,
+ if($what =~ /media/) {
+ push(@{$r->[$r{$_->{xid}}]{media}}, $_) for (@{$self->dbAll("
+ SELECT $colname AS xid, medium, qty
+ FROM releases_media$hist
+ WHERE $colname IN(!l)",
[ keys %r ]
)});
}
@@ -179,38 +203,38 @@ sub dbReleaseRevisionInsert {
my %set = map exists($o->{$_}) ? ("$_ = ?", $o->{$_}) : (),
qw|title original gtin catalog website released notes minage type
patch resolution voiced freeware doujin ani_story ani_ero|;
- $self->dbExec('UPDATE edit_release !H', \%set) if keys %set;
+ $self->dbExec('UPDATE edit_releases !H', \%set) if keys %set;
if($o->{languages}) {
- $self->dbExec('DELETE FROM edit_release_lang');
+ $self->dbExec('DELETE FROM edit_releases_lang');
my $q = join ',', map '(?)', @{$o->{languages}};
- $self->dbExec("INSERT INTO edit_release_lang (lang) VALUES $q", @{$o->{languages}}) if @{$o->{languages}};
+ $self->dbExec("INSERT INTO edit_releases_lang (lang) VALUES $q", @{$o->{languages}}) if @{$o->{languages}};
}
if($o->{producers}) {
- $self->dbExec('DELETE FROM edit_release_producers');
+ $self->dbExec('DELETE FROM edit_releases_producers');
my $q = join ',', map '(?,?,?)', @{$o->{producers}};
my @q = map +($_->[0], $_->[1]?1:0, $_->[2]?1:0), @{$o->{producers}};
- $self->dbExec("INSERT INTO edit_release_producers (pid, developer, publisher) VALUES $q", @q) if @q;
+ $self->dbExec("INSERT INTO edit_releases_producers (pid, developer, publisher) VALUES $q", @q) if @q;
}
if($o->{platforms}) {
- $self->dbExec('DELETE FROM edit_release_platforms');
+ $self->dbExec('DELETE FROM edit_releases_platforms');
my $q = join ',', map '(?)', @{$o->{platforms}};
- $self->dbExec("INSERT INTO edit_release_platforms (platform) VALUES $q", @{$o->{platforms}}) if @{$o->{platforms}};
+ $self->dbExec("INSERT INTO edit_releases_platforms (platform) VALUES $q", @{$o->{platforms}}) if @{$o->{platforms}};
}
if($o->{vn}) {
- $self->dbExec('DELETE FROM edit_release_vn');
+ $self->dbExec('DELETE FROM edit_releases_vn');
my $q = join ',', map '(?)', @{$o->{vn}};
- $self->dbExec("INSERT INTO edit_release_vn (vid) VALUES $q", @{$o->{vn}}) if @{$o->{vn}};
+ $self->dbExec("INSERT INTO edit_releases_vn (vid) VALUES $q", @{$o->{vn}}) if @{$o->{vn}};
}
if($o->{media}) {
- $self->dbExec('DELETE FROM edit_release_media');
+ $self->dbExec('DELETE FROM edit_releases_media');
my $q = join ',', map '(?,?)', @{$o->{media}};
my @q = map +($_->[0], $_->[1]), @{$o->{media}};
- $self->dbExec("INSERT INTO edit_release_media (medium, qty) VALUES $q", @q) if @q;
+ $self->dbExec("INSERT INTO edit_releases_media (medium, qty) VALUES $q", @q) if @q;
}
}
diff --git a/lib/VNDB/DB/Staff.pm b/lib/VNDB/DB/Staff.pm
index 4dfe0756..69f4d6b3 100644
--- a/lib/VNDB/DB/Staff.pm
+++ b/lib/VNDB/DB/Staff.pm
@@ -5,9 +5,9 @@ use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw|dbStaffGet dbStaffRevisionInsert dbStaffAliasIds|;
+our @EXPORT = qw|dbStaffGet dbStaffGetRev dbStaffRevisionInsert dbStaffAliasIds|;
-# options: results, page, id, aid, search, exact, rev, truename, role, gender
+# options: results, page, id, aid, search, exact, truename, role, gender
# what: extended changes roles aliases
sub dbStaffGet {
my $self = shift;
@@ -31,15 +31,15 @@ sub dbStaffGet {
$o{search} =~ s/%//g if $o{search};
my %where = (
- !$o{id} && !$o{rev} ? ( 's.hidden = FALSE' => 1 ) : (),
+ !$o{id} ? ( 's.hidden = FALSE' => 1 ) : (),
$o{id} ? ( ref $o{id} ? ('s.id IN(!l)' => [$o{id}]) : ('s.id = ?' => $o{id}) ) : (),
- $o{aid} ? ( ref $o{aid} ? ('sa.id IN(!l)' => [$o{aid}]) : ('sa.id = ?' => $o{aid}) ) : (),
- $o{truename} ? ( 'sr.aid = sa.id' => 1 ) : (),
- defined $o{gender} ? ( 'sr.gender IN(!l)' => [ ref $o{gender} ? $o{gender} : [$o{gender}] ]) : (),
+ $o{aid} ? ( ref $o{aid} ? ('sa.aid IN(!l)' => [$o{aid}]) : ('sa.aid = ?' => $o{aid}) ) : (),
+ $o{id} || $o{truename} ? ( 's.aid = sa.aid' => 1 ) : (),
+ defined $o{gender} ? ( 's.gender IN(!l)' => [ ref $o{gender} ? $o{gender} : [$o{gender}] ]) : (),
defined $o{role} ? (
'('.join(' OR ',
- @roles ? ( 'EXISTS(SELECT 1 FROM vn_staff vs JOIN vn v ON v.latest = vs.vid WHERE vs.aid = sa.id AND vs.role IN(!l) AND NOT v.hidden)' ) : (),
- $seiyuu ? ( 'EXISTS(SELECT 1 FROM vn_seiyuu vsy JOIN vn v ON v.latest = vsy.vid WHERE vsy.aid = sa.id AND NOT v.hidden)' ) : ()
+ @roles ? ( 'EXISTS(SELECT 1 FROM vn_staff vs JOIN vn v ON v.id = vs.id WHERE vs.aid = sa.aid AND vs.role IN(!l) AND NOT v.hidden)' ) : (),
+ $seiyuu ? ( 'EXISTS(SELECT 1 FROM vn_seiyuu vsy JOIN vn v ON v.id = vsy.id WHERE vsy.aid = sa.aid AND NOT v.hidden)' ) : ()
).')' => ( @roles ? [ \@roles ] : 1 ),
) : (),
$o{exact} ? ( '(sa.name = ? OR sa.original = ?)' => [ ($o{exact}) x 2 ] ) : (),
@@ -53,87 +53,114 @@ sub dbStaffGet {
$o{char} ? ( 'LOWER(SUBSTR(sa.name, 1, 1)) = ?' => $o{char} ) : (),
defined $o{char} && !$o{char} ?
( '(ASCII(sa.name) < 97 OR ASCII(sa.name) > 122) AND (ASCII(sa.name) < 65 OR ASCII(sa.name) > 90)' => 1 ) : (),
- $o{rev} ? ( 'c.rev = ?' => $o{rev} ) : (),
);
- my @join;
- push @join, 'JOIN staff s ON '.($o{rev} ? 's.id = sr.sid' : 'sr.id = s.latest');
- push @join, 'JOIN staff_alias sa ON sa.rid = sr.id'.($o{id}?' AND sa.id = sr.aid':'');
- push @join, 'JOIN changes c ON c.id = sr.id' if $o{what} =~ /changes/ || $o{rev};
- push @join, 'JOIN users u ON u.id = c.requester' if $o{what} =~ /changes/;
-
- my $select = 's.id, sa.id AS aid, sa.name, sa.original, sr.gender, sr.lang, sr.id AS cid';
- $select .= ', sr.desc, sr.l_wp, sr.l_site, sr.l_twitter, sr.l_anidb, s.hidden, s.locked' if $o{what} =~ /extended/;
- $select .= q|, extract('epoch' from c.added) as added, c.requester, c.comments, s.latest, u.username, c.rev, c.ihid, c.ilock| if $o{what} =~ /changes/;
-
- my $order = 'ORDER BY sa.name';
+ my $select = 's.id, sa.aid, sa.name, sa.original, s.gender, s.lang';
+ $select .= ', s.desc, s.l_wp, s.l_site, s.l_twitter, s.l_anidb, s.hidden, s.locked' if $o{what} =~ /extended/;
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
- FROM staff_rev sr
- !s
+ FROM staff s
+ JOIN staff_alias sa ON sa.id = s.id
!W
- !s|,
- $select, join(' ', @join), \%where, $order
+ ORDER BY sa.name|,
+ $select, \%where
);
- if (@$r && $o{what} =~ /roles|aliases/) {
+ return _enrich($self, $r, $np, 0, $o{what});
+}
+
+
+sub dbStaffGetRev {
+ my $self = shift;
+ my %o = (what => '', @_);
+
+ $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'s\' AND itemid = ?', $o{id})->{rev};
+
+ my $select = 'c.itemid AS id, sa.aid, sa.name, sa.original, s.gender, s.lang';
+ $select .= ', extract(\'epoch\' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock';
+ $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
+ $select .= ', s.desc, s.l_wp, s.l_site, s.l_twitter, s.l_anidb, so.hidden, so.locked' if $o{what} =~ /extended/;
+
+ my $r = $self->dbAll(q|
+ SELECT !s
+ FROM changes c
+ JOIN staff so ON so.id = c.itemid
+ JOIN staff_hist s ON s.chid = c.id
+ JOIN staff_alias_hist sa ON sa.chid = c.id AND s.aid = sa.aid
+ JOIN users u ON u.id = c.requester
+ WHERE c.type = 's' AND c.itemid = ? AND c.rev = ?|,
+ $select, $o{id}, $o{rev}
+ );
+
+ return _enrich($self, $r, 0, 1, $o{what});
+}
+
+
+sub _enrich {
+ my($self, $r, $np, $rev, $what) = @_;
+
+ # Role info is linked to VN revisions, so is independent of the selected staff revision
+ if(@$r && $what =~ /roles/) {
my %r = map {
$_->{roles} = [];
$_->{cast} = [];
+ ($_->{id}, $_);
+ } @$r;
+
+ push @{$r{ delete $_->{id} }{roles}}, $_ for (@{$self->dbAll(q|
+ SELECT sa.id, sa.aid, v.id AS vid, sa.name, sa.original, v.c_released, v.title, v.original AS t_original, vs.role, vs.note
+ FROM vn_staff vs
+ JOIN vn v ON v.id = vs.id
+ JOIN staff_alias sa ON vs.aid = sa.aid
+ WHERE sa.id IN(!l) AND NOT v.hidden
+ ORDER BY v.c_released ASC, v.title ASC, vs.role ASC|, [ keys %r ]
+ )});
+ push @{$r{ delete $_->{id} }{cast}}, $_ for (@{$self->dbAll(q|
+ SELECT sa.id, sa.aid, v.id AS vid, sa.name, sa.original, v.c_released, v.title, v.original AS t_original, c.id AS cid, c.name AS c_name, c.original AS c_original, vs.note
+ FROM vn_seiyuu vs
+ JOIN vn v ON v.id = vs.id
+ JOIN chars c ON c.id = vs.cid
+ JOIN staff_alias sa ON vs.aid = sa.aid
+ WHERE sa.id IN(!l) AND NOT v.hidden
+ ORDER BY v.c_released ASC, v.title ASC|, [ keys %r ]
+ )});
+ }
+
+ if(@$r && $what =~ /aliases/) {
+ my ($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
+ my %r = map {
$_->{aliases} = [];
- ($_->{cid}, $_);
+ ($_->{$col}, $_);
} @$r;
- if ($o{what} =~ /roles/) {
- push @{$r{ delete $_->{rid} }{roles}}, $_ for (@{$self->dbAll(q|
- SELECT sa.rid, vr.vid, sa.name, sa.original, v.c_released, vr.title, vr.original AS t_original, vs.role, vs.note
- FROM vn_staff vs
- JOIN vn_rev vr ON vr.id = vs.vid
- JOIN vn v ON v.latest = vr.id
- JOIN staff_alias sa ON vs.aid = sa.id
- WHERE sa.rid IN(!l) AND NOT v.hidden
- ORDER BY v.c_released ASC, vr.title ASC, vs.role ASC|, [ keys %r ]
- )});
- push @{$r{ delete $_->{rid} }{cast}}, $_ for (@{$self->dbAll(q|
- SELECT sa.rid, vr.vid, sa.name, sa.original, v.c_released, vr.title, vr.original AS t_original, cr.cid, cr.name AS c_name, cr.original AS c_original, vs.note
- FROM vn_seiyuu vs
- JOIN vn_rev vr ON vr.id = vs.vid
- JOIN vn v ON v.latest = vr.id
- JOIN chars_rev cr ON cr.cid = vs.cid
- JOIN chars c ON c.latest = cr.id
- JOIN staff_alias sa ON vs.aid = sa.id
- WHERE sa.rid IN(!l) AND NOT v.hidden
- ORDER BY v.c_released ASC, vr.title ASC|, [ keys %r ]
- )});
- }
- if ($o{what} =~ /aliases/) {
- push @{$r{ delete $_->{rid} }{aliases}}, $_ for (@{$self->dbAll(q|
- SELECT sa.id, sa.rid, sa.name, sa.original
- FROM staff_alias sa
- JOIN staff_rev sr ON sr.id = sa.rid
- WHERE sr.id IN(!l) AND sr.aid <> sa.id
- ORDER BY sa.name ASC|, [ keys %r ]
- )});
- }
+
+ push @{$r{ delete $_->{xid} }{aliases}}, $_ for (@{$self->dbAll("
+ SELECT s.$colname AS xid, sa.aid, sa.name, sa.original
+ FROM staff_alias$hist sa
+ JOIN staff$hist s ON s.$colname = sa.$colname
+ WHERE s.$colname IN(!l) AND s.aid <> sa.aid
+ ORDER BY sa.name ASC", [ keys %r ]
+ )});
}
return wantarray ? ($r, $np) : $r;
}
+
# Updates the edit_* tables, used from dbItemEdit()
# Arguments: { columns in staff_rev and staff_alias},
sub dbStaffRevisionInsert {
my($self, $o) = @_;
- $self->dbExec('DELETE FROM edit_staff_aliases');
+ $self->dbExec('DELETE FROM edit_staff_alias');
if($o->{aid}) {
$self->dbExec(q|
- INSERT INTO edit_staff_aliases (id, name, original) VALUES (?, ?, ?)|,
+ INSERT INTO edit_staff_alias (aid, name, original) VALUES (?, ?, ?)|,
$o->{aid}, $o->{name}, $o->{original});
} else {
$o->{aid} = $self->dbRow(q|
- INSERT INTO edit_staff_aliases (name, original) VALUES (?, ?) RETURNING id|,
- $o->{name}, $o->{original})->{id};
+ INSERT INTO edit_staff_alias (name, original) VALUES (?, ?) RETURNING aid|,
+ $o->{name}, $o->{original})->{aid};
}
my %staff = map exists($o->{$_}) ? (qq|"$_" = ?|, $o->{$_}) : (),
@@ -141,9 +168,9 @@ sub dbStaffRevisionInsert {
$self->dbExec('UPDATE edit_staff !H', \%staff) if %staff;
for my $a (@{$o->{aliases}}) {
if($a->{aid}) {
- $self->dbExec('INSERT INTO edit_staff_aliases (id, name, original) VALUES (!l)', [ @{$a}{qw|aid name orig|} ]);
+ $self->dbExec('INSERT INTO edit_staff_alias (aid, name, original) VALUES (!l)', [ @{$a}{qw|aid name orig|} ]);
} else {
- $self->dbExec('INSERT INTO edit_staff_aliases (name, original) VALUES (?, ?)', $a->{name}, $a->{orig});
+ $self->dbExec('INSERT INTO edit_staff_alias (name, original) VALUES (?, ?)', $a->{name}, $a->{orig});
}
}
}
@@ -153,10 +180,10 @@ sub dbStaffRevisionInsert {
sub dbStaffAliasIds {
my($self, $sid) = @_;
return $self->dbAll(q|
- SELECT DISTINCT sa.id
- FROM staff_alias sa
- JOIN staff_rev sr ON sr.id = sa.rid
- WHERE sr.sid = ?|, $sid);
+ SELECT DISTINCT sa.aid
+ FROM changes c
+ JOIN staff_alias_hist sa ON sa.chid = c.id
+ WHERE c.type = 's' AND c.itemid = ?|, $sid);
}
1;
diff --git a/lib/VNDB/DB/Tags.pm b/lib/VNDB/DB/Tags.pm
index 37411a62..f3c901ab 100644
--- a/lib/VNDB/DB/Tags.pm
+++ b/lib/VNDB/DB/Tags.pm
@@ -184,11 +184,11 @@ sub dbTagLinks {
my @select = (
qw|tv.tag tv.vid tv.uid tv.vote tv.spoiler tv.ignore|, "EXTRACT('epoch' from tv.date) AS date",
- $o{what} =~ /details/ ? (qw|vr.title u.username t.name|) : (),
+ $o{what} =~ /details/ ? (qw|v.title u.username t.name|) : (),
);
my @join = $o{what} =~ /details/ ? (
- 'JOIN vn v ON v.id = tv.vid JOIN vn_rev vr ON vr.id = v.latest',
+ 'JOIN vn v ON v.id = tv.vid',
'JOIN users u ON u.id = tv.uid',
'JOIN tags t ON t.id = tv.tag'
) : ();
@@ -196,7 +196,7 @@ sub dbTagLinks {
my $order = !$o{sort} ? '' : 'ORDER BY '.{
username => 'u.username',
date => 'tv.date',
- title => 'vr.title',
+ title => 'v.title',
tag => 't.name',
}->{$o{sort}}.($o{reverse} ? ' DESC' : ' ASC');
diff --git a/lib/VNDB/DB/ULists.pm b/lib/VNDB/DB/ULists.pm
index 6831e579..23bde7cf 100644
--- a/lib/VNDB/DB/ULists.pm
+++ b/lib/VNDB/DB/ULists.pm
@@ -59,22 +59,21 @@ sub dbVNListList {
'vl.uid = ?' => $o{uid},
defined($o{voted}) ? ('vo.vote !s NULL' => $o{voted} ? 'IS NOT' : 'IS') : (),
defined($o{status})? ('vl.status = ?' => $o{status}) : (),
- $o{char} ? ('LOWER(SUBSTR(vr.title, 1, 1)) = ?' => $o{char} ) : (),
+ $o{char} ? ('LOWER(SUBSTR(v.title, 1, 1)) = ?' => $o{char} ) : (),
defined $o{char} && !$o{char} ? (
- '(ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)' => 1 ) : (),
+ '(ASCII(v.title) < 97 OR ASCII(v.title) > 122) AND (ASCII(v.title) < 65 OR ASCII(v.title) > 90)' => 1 ) : (),
);
my $order = sprintf {
- title => 'vr.title %s',
- vote => 'vo.vote %s NULLS LAST, vr.title ASC',
+ title => 'v.title %s',
+ vote => 'vo.vote %s NULLS LAST, v.title ASC',
}->{ $o{sort}||'title' }, $o{reverse} ? 'DESC' : 'ASC';
# execute query
my($r, $np) = $self->dbPage(\%o, qq|
- SELECT vr.vid, vr.title, vr.original, vl.status, vl.notes, COALESCE(vo.vote, 0) AS vote
+ SELECT vl.vid, v.title, v.original, vl.status, vl.notes, COALESCE(vo.vote, 0) AS vote
FROM vnlists vl
JOIN vn v ON v.id = vl.vid
- JOIN vn_rev vr ON vr.id = v.latest
LEFT JOIN votes vo ON vo.vid = vl.vid AND vo.uid = vl.uid
!W
ORDER BY !s|,
@@ -89,27 +88,26 @@ sub dbVNListList {
} @$r;
my $rel = $self->dbAll(q|
- SELECT rv.vid, rr.rid, r.latest, rr.title, rr.original, rr.released, rr.type, rl.status
+ SELECT rv.vid, rl.rid, r.title, r.original, r.released, r.type, rl.status
FROM rlists rl
JOIN releases r ON rl.rid = r.id
- JOIN releases_rev rr ON rr.id = r.latest
- JOIN releases_vn rv ON rv.rid = r.latest
+ JOIN releases_vn rv ON rv.id = r.id
WHERE rl.uid = ?
AND rv.vid IN(!l)
- ORDER BY rr.released ASC|,
+ ORDER BY r.released ASC|,
$o{uid}, [ keys %vns ]
);
if(@$rel) {
- my %rel = map { $_->{latest} => [] } @$rel;
- push(@{$rel{$_->{rid}}}, $_->{lang}) for (@{$self->dbAll(q|
- SELECT rid, lang
+ my %rel = map { $_->{rid} => [] } @$rel;
+ push(@{$rel{$_->{id}}}, $_->{lang}) for (@{$self->dbAll(q|
+ SELECT id, lang
FROM releases_lang
- WHERE rid IN(!l)|,
+ WHERE id IN(!l)|,
[ keys %rel ]
)});
for(@$rel) {
- $_->{languages} = $rel{$_->{latest}};
+ $_->{languages} = $rel{$_->{rid}};
push @{$vns{$_->{vid}}}, $_;
}
}
@@ -189,9 +187,9 @@ sub dbVoteGet {
$o{vid} ? ( 'n.vid = ?' => $o{vid} ) : (),
$o{hide} ? ( 'NOT EXISTS(SELECT 1 FROM users_prefs WHERE uid = n.uid AND key = \'hide_list\')' => 1 ) : (),
$o{hide_ign} ? ( '(NOT u.ign_votes OR u.id = ?)' => $self->authInfo->{id}||0 ) : (),
- $o{vn_char} ? ( 'LOWER(SUBSTR(vr.title, 1, 1)) = ?' => $o{vn_char} ) : (),
+ $o{vn_char} ? ( 'LOWER(SUBSTR(v.title, 1, 1)) = ?' => $o{vn_char} ) : (),
defined $o{vn_char} && !$o{vn_char} ? (
- '(ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)' => 1 ) : (),
+ '(ASCII(v.title) < 97 OR ASCII(v.title) > 122) AND (ASCII(v.title) < 65 OR ASCII(v.title) > 90)' => 1 ) : (),
$o{user_char} ? ( 'LOWER(SUBSTR(u.username, 1, 1)) = ?' => $o{user_char} ) : (),
defined $o{user_char} && !$o{user_char} ? (
'(ASCII(u.username) < 97 OR ASCII(u.username) > 122) AND (ASCII(u.username) < 65 OR ASCII(u.username) > 90)' => 1 ) : (),
@@ -200,13 +198,12 @@ sub dbVoteGet {
my @select = (
qw|n.vid n.vote n.uid|, q|extract('epoch' from n.date) as date|,
$o{what} =~ /user/ ? ('u.username') : (),
- $o{what} =~ /vn/ ? (qw|vr.title vr.original|) : (),
+ $o{what} =~ /vn/ ? (qw|v.title v.original|) : (),
);
my @join = (
$o{what} =~ /vn/ ? (
'JOIN vn v ON v.id = n.vid',
- 'JOIN vn_rev vr ON vr.id = v.latest'
) : (),
$o{what} =~ /user/ || $o{hide} ? (
'JOIN users u ON u.id = n.uid'
@@ -216,8 +213,8 @@ sub dbVoteGet {
my $order = sprintf {
date => 'n.date %s',
username => 'u.username %s',
- title => 'vr.title %s',
- vote => 'n.vote %s'.($o{what} =~ /vn/ ? ', vr.title ASC' : $o{what} =~ /user/ ? ', u.username ASC' : ''),
+ title => 'v.title %s',
+ vote => 'n.vote %s'.($o{what} =~ /vn/ ? ', v.title ASC' : $o{what} =~ /user/ ? ', u.username ASC' : ''),
}->{$o{sort}}, $o{reverse} ? 'DESC' : 'ASC';
my($r, $np) = $self->dbPage(\%o, q|
@@ -302,15 +299,14 @@ sub dbWishListGet {
my $select = q|wl.vid, wl.wstat, extract('epoch' from wl.added) AS added|;
my @join;
if($o{what} =~ /vn/) {
- $select .= ', vr.title, vr.original';
- push @join, 'JOIN vn v ON v.id = wl.vid',
- 'JOIN vn_rev vr ON vr.id = v.latest';
+ $select .= ', v.title, v.original';
+ push @join, 'JOIN vn v ON v.id = wl.vid';
}
my $order = sprintf {
- title => 'vr.title %s',
+ title => 'v.title %s',
added => 'wl.added %s',
- wstat => 'wl.wstat %2$s, vr.title ASC',
+ wstat => 'wl.wstat %2$s, v.title ASC',
}->{ $o{sort}||'added' }, $o{reverse} ? 'DESC' : 'ASC', $o{reverse} ? 'ASC' : 'DESC';
my($r, $np) = $self->dbPage(\%o, q|
diff --git a/lib/VNDB/DB/VN.pm b/lib/VNDB/DB/VN.pm
index 5d404b28..8f3bf913 100644
--- a/lib/VNDB/DB/VN.pm
+++ b/lib/VNDB/DB/VN.pm
@@ -7,12 +7,12 @@ use Exporter 'import';
use VNDB::Func 'gtintype', 'normalize_query';
use Encode 'decode_utf8';
-our @EXPORT = qw|dbVNGet dbVNRevisionInsert dbVNImageId dbScreenshotAdd dbScreenshotGet dbScreenshotRandom dbVNHasChar dbVNHasStaff dbVNImportSeiyuu|;
+our @EXPORT = qw|dbVNGet dbVNGetRev dbVNRevisionInsert dbVNImageId dbScreenshotAdd dbScreenshotGet dbScreenshotRandom dbVNHasChar dbVNHasStaff dbVNImportSeiyuu|;
-# Options: id, rev, char, search, length, lang, olang, plat, tag_inc, tag_exc, tagspoil,
+# Options: id, char, search, length, lang, olang, plat, tag_inc, tag_exc, tagspoil,
# hasani, hasshot, ul_notblack, ul_onwish, results, page, what, sort, reverse, inc_hidden
-# What: extended anime credits relations screenshots relgraph rating ranking changes wishlist vnlist
+# What: extended anime credits relations screenshots relgraph rating ranking wishlist vnlist
# Note: wishlist and vnlist are ignored (no db search) unless a user is logged in
# Sort: id rel pop rating title tagscore rand
sub dbVNGet {
@@ -33,14 +33,12 @@ sub dbVNGet {
my @where = (
$o{id} ? (
'v.id IN(!l)' => [ ref $o{id} ? $o{id} : [$o{id}] ] ) : (),
- $o{rev} ? (
- 'c.rev = ?' => $o{rev} ) : (),
$o{char} ? (
- 'LOWER(SUBSTR(vr.title, 1, 1)) = ?' => $o{char} ) : (),
+ 'LOWER(SUBSTR(v.title, 1, 1)) = ?' => $o{char} ) : (),
defined $o{char} && !$o{char} ? (
- '(ASCII(vr.title) < 97 OR ASCII(vr.title) > 122) AND (ASCII(vr.title) < 65 OR ASCII(vr.title) > 90)' => 1 ) : (),
+ '(ASCII(v.title) < 97 OR ASCII(v.title) > 122) AND (ASCII(v.title) < 65 OR ASCII(v.title) > 90)' => 1 ) : (),
defined $o{length} ? (
- 'vr.length IN(!l)' => [ ref $o{length} ? $o{length} : [$o{length}] ]) : (),
+ 'v.length IN(!l)' => [ ref $o{length} ? $o{length} : [$o{length}] ]) : (),
$o{lang} ? (
'v.c_languages && ARRAY[!l]::language[]' => [ ref $o{lang} ? $o{lang} : [$o{lang}] ]) : (),
$o{olang} ? (
@@ -48,9 +46,9 @@ sub dbVNGet {
$o{plat} ? (
'v.c_platforms && ARRAY[!l]::platform[]' => [ ref $o{plat} ? $o{plat} : [$o{plat}] ]) : (),
defined $o{hasani} ? (
- '!sEXISTS(SELECT 1 FROM vn_anime va WHERE va.vid = vr.id)' => [ $o{hasani} ? '' : 'NOT ' ]) : (),
+ '!sEXISTS(SELECT 1 FROM vn_anime va WHERE va.id = v.id)' => [ $o{hasani} ? '' : 'NOT ' ]) : (),
defined $o{hasshot} ? (
- '!sEXISTS(SELECT 1 FROM vn_screenshots vs WHERE vs.vid = vr.id)' => [ $o{hasshot} ? '' : 'NOT ' ]) : (),
+ '!sEXISTS(SELECT 1 FROM vn_screenshots vs WHERE vs.id = v.id)' => [ $o{hasshot} ? '' : 'NOT ' ]) : (),
$o{tag_inc} ? (
'v.id IN(SELECT vid FROM tags_vn_inherit WHERE tag IN(!l) AND spoiler <= ? GROUP BY vid HAVING COUNT(tag) = ?)',
[ ref $o{tag_inc} ? $o{tag_inc} : [$o{tag_inc}], $o{tagspoil}, ref $o{tag_inc} ? $#{$o{tag_inc}}+1 : 1 ]) : (),
@@ -66,24 +64,14 @@ sub dbVNGet {
'v.id !s IN(SELECT vid FROM votes WHERE uid = ?)' => [ $o{ul_voted} ? '' : 'NOT', $uid ] ) : (),
$uid && defined $o{ul_onlist} ? (
'v.id !s IN(SELECT vid FROM vnlists WHERE uid = ?)' => [ $o{ul_onlist} ? '' : 'NOT', $uid ] ) : (),
- # don't fetch hidden items unless we ask for an ID
- !$o{id} && !$o{rev} && !$o{inc_hidden} ? (
+ !$o{id} && !$o{inc_hidden} ? (
'v.hidden = FALSE' => 0 ) : (),
- # optimize fetching random entries (only when there are no other filters present, otherwise this won't work well)
+ # optimize fetching random entries (only when there are no other filters present, otherwise this won't work well)
$o{sort} eq 'rand' && $o{results} <= 10 && !grep(!/^(?:results|page|what|sort|tagspoil)$/, keys %o) ? (
- sprintf 'v.id IN(SELECT floor(random() * last_value)::integer
- FROM generate_series(1,20), (SELECT last_value FROM vn_id_seq) s1
- LIMIT 20)' ) : (),
+ 'v.id IN(SELECT floor(random() * last_value)::integer FROM generate_series(1,20), (SELECT MAX(id) AS last_value FROM vn) s1 LIMIT 20)' ) : (),
);
my @join = (
- $o{rev} ?
- 'JOIN vn v ON v.id = vr.vid' :
- 'JOIN vn v ON vr.id = v.latest',
- $o{rev} || $o{what} =~ /changes/ ?
- 'JOIN changes c ON c.id = vr.id' : (),
- $o{what} =~ /changes/ ?
- 'JOIN users u ON u.id = c.requester' : (),
$o{what} =~ /relgraph/ ?
'JOIN relgraphs vg ON vg.id = v.rgraph' : (),
$uid && $o{what} =~ /wishlist/ ?
@@ -92,8 +80,7 @@ sub dbVNGet {
SELECT irv.vid, COUNT(*) AS userlist_all,
SUM(CASE WHEN irl.status = 2 THEN 1 ELSE 0 END) AS userlist_obtained
FROM rlists irl
- JOIN releases ir ON irl.rid = ir.id
- JOIN releases_vn irv ON irv.rid = ir.latest
+ JOIN releases_vn irv ON irv.id = irl.rid
WHERE irl.uid = $uid
GROUP BY irv.vid
) AS vnlist ON vnlist.vid = v.id") : (),
@@ -101,11 +88,9 @@ sub dbVNGet {
my $tag_ids = $o{tag_inc} && join ',', ref $o{tag_inc} ? @{$o{tag_inc}} : $o{tag_inc};
my @select = ( # see https://rt.cpan.org/Ticket/Display.html?id=54224 for the cast on c_languages and c_platforms
- qw|v.id v.locked v.hidden v.c_released v.c_languages::text[] v.c_platforms::text[] vr.title vr.original v.rgraph|, 'vr.id AS cid',
+ qw|v.id v.locked v.hidden v.c_released v.c_languages::text[] v.c_platforms::text[] v.title v.original v.rgraph|,
$o{what} =~ /extended/ ? (
- qw|vr.alias vr.image vr.img_nsfw vr.length vr.desc vr.l_wp vr.l_encubed vr.l_renai| ) : (),
- $o{what} =~ /changes/ ? (
- qw|c.requester c.comments v.latest u.username c.rev c.ihid c.ilock|, q|extract('epoch' from c.added) as added|) : (),
+ qw|v.alias v.image v.img_nsfw v.length v.desc v.l_wp v.l_encubed v.l_renai| ) : (),
$o{what} =~ /relgraph/ ? 'vg.svg' : (),
$o{what} =~ /rating/ ? (qw|v.c_popularity v.c_rating v.c_votecount|) : (),
$o{what} =~ /ranking/ ? (
@@ -120,100 +105,129 @@ sub dbVNGet {
);
my $order = sprintf {
- id => 'id %s',
- rel => 'c_released %s, title ASC',
- pop => 'c_popularity %s NULLS LAST',
- rating => 'c_rating %s NULLS LAST',
- title => 'title %s',
+ id => 'v.id %s',
+ rel => 'v.c_released %s, v.title ASC',
+ pop => 'v.c_popularity %s NULLS LAST',
+ rating => 'v.c_rating %s NULLS LAST',
+ title => 'v.title %s',
tagscore => 'tagscore %s',
rand => 'RANDOM()',
}->{$o{sort}}, $o{reverse} ? 'DESC' : 'ASC';
my($r, $np) = $self->dbPage(\%o, q|
SELECT !s
- FROM vn_rev vr
+ FROM vn v
!s
!W
ORDER BY !s|,
join(', ', @select), join(' ', @join), \@where, $order,
);
- if($o{what} =~ /relgraph/) {
- $_->{svg} = decode_utf8($_->{svg}) for @$r;
- }
+ $_->{svg} && ($_->{svg} = decode_utf8($_->{svg})) for (@$r);
+ return _enrich($self, $r, $np, 0, $o{what});
+}
+
+
+sub dbVNGetRev {
+ my $self = shift;
+ my %o = (what => '', @_);
+
+ $o{rev} ||= $self->dbRow('SELECT MAX(rev) AS rev FROM changes WHERE type = \'v\' AND itemid = ?', $o{id})->{rev};
- if(@$r && $o{what} =~ /anime|relations|screenshots|credits/) {
+ # XXX: Too much duplication with code in dbVNGet() here. Can we combine some code here?
+ my $uid = $self->authInfo->{id};
+
+ my $select = 'c.itemid AS id, vo.c_released, vo.c_languages::text[], vo.c_platforms::text[], v.title, v.original, vo.rgraph';
+ $select .= ', extract(\'epoch\' from c.added) as added, c.requester, c.comments, u.username, c.rev, c.ihid, c.ilock';
+ $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev';
+ $select .= ', v.alias, v.image, v.img_nsfw, v.length, v.desc, v.l_wp, v.l_encubed, v.l_renai, vo.hidden, vo.locked' if $o{what} =~ /extended/;
+ $select .= ', vo.c_popularity, vo.c_rating, vo.c_votecount' if $o{what} =~ /rating/;
+ $select .= ', (SELECT COUNT(*)+1 FROM vn iv WHERE iv.hidden = false AND iv.c_popularity > COALESCE(vo.c_popularity, 0.0)) AS p_ranking'
+ .', (SELECT COUNT(*)+1 FROM vn iv WHERE iv.hidden = false AND iv.c_rating > COALESCE(vo.c_rating, 0.0)) AS r_ranking' if $o{what} =~ /ranking/;
+
+ my $r = $self->dbAll(q|
+ SELECT !s
+ FROM changes c
+ JOIN vn vo ON vo.id = c.itemid
+ JOIN vn_hist v ON v.chid = c.id
+ JOIN users u ON u.id = c.requester
+ WHERE c.type = 'v' AND c.itemid = ? AND c.rev = ?|,
+ $select, $o{id}, $o{rev}
+ );
+
+ return _enrich($self, $r, 0, 1, $o{what});
+}
+
+
+sub _enrich {
+ my($self, $r, $np, $rev, $what) = @_;
+
+ if(@$r && $what =~ /anime|relations|screenshots|credits/) {
+ my($col, $hist, $colname) = $rev ? ('cid', '_hist', 'chid') : ('id', '', 'id');
my %r = map {
$r->[$_]{anime} = [];
$r->[$_]{credits} = [];
$r->[$_]{seiyuu} = [];
$r->[$_]{relations} = [];
$r->[$_]{screenshots} = [];
- ($r->[$_]{cid}, $_)
+ ($r->[$_]{$col}, $_)
} 0..$#$r;
- if($o{what} =~ /credits/) {
- push(@{$r->[$r{ delete $_->{vid} }]{credits}}, $_) for (@{$self->dbAll(q|
- SELECT vs.vid, s.id, vs.aid, sa.name, sa.original, sr.gender, sr.lang, vs.role, vs.note
- FROM vn_staff vs
- JOIN staff_alias sa ON vs.aid = sa.id
- JOIN staff_rev sr ON sr.id = sa.rid
- JOIN staff s ON sr.id = s.latest
- WHERE s.hidden = FALSE AND vs.vid IN(!l)
- ORDER BY vs.role ASC, sa.name ASC|,
+ if($what =~ /credits/) {
+ push(@{$r->[$r{ delete $_->{xid} }]{credits}}, $_) for (@{$self->dbAll("
+ SELECT vs.$colname AS xid, s.id, vs.aid, sa.name, sa.original, s.gender, s.lang, vs.role, vs.note
+ FROM vn_staff$hist vs
+ JOIN staff_alias sa ON vs.aid = sa.aid
+ JOIN staff s ON s.id = sa.id
+ WHERE s.hidden = FALSE AND vs.$colname IN(!l)
+ ORDER BY vs.role ASC, sa.name ASC",
[ keys %r ]
)});
- push(@{$r->[$r{ delete $_->{vid} }]{seiyuu}}, $_) for (@{$self->dbAll(q|
- SELECT vs.vid, s.id, vs.aid, sa.name, sa.original, sr.gender, sr.lang, cr.cid, cr.name AS cname, vs.note,
- (SELECT MAX(spoil) FROM chars_vns cv WHERE cv.vid = vr.vid AND cv.cid = cr.id) AS spoil
- FROM vn_seiyuu vs
- JOIN vn_rev vr ON vr.id = vs.vid
- JOIN staff_alias sa ON vs.aid = sa.id
- JOIN staff_rev sr ON sr.id = sa.rid
- JOIN staff s ON s.id = sr.sid AND sr.id = s.latest
+
+ # The seiyuu query needs the VN id to get the VN<->Char spoiler level.
+ # Obtaining this ID is different when using the hist table.
+ my($vid, $join) = $rev ? ('h.itemid', 'JOIN changes h ON h.id = vs.chid') : ('vs.id', '');
+ push(@{$r->[$r{ delete $_->{xid} }]{seiyuu}}, $_) for (@{$self->dbAll("
+ SELECT vs.$colname AS xid, s.id, vs.aid, sa.name, sa.original, s.gender, s.lang, c.id AS cid, c.name AS cname, vs.note,
+ (SELECT MAX(spoil) FROM chars_vns cv WHERE cv.vid = $vid AND cv.id = c.id) AS spoil
+ FROM vn_seiyuu$hist vs
+ JOIN staff_alias sa ON vs.aid = sa.aid
+ JOIN staff s ON s.id = sa.id
JOIN chars c ON c.id = vs.cid
- JOIN chars_rev cr ON cr.id = c.latest
- WHERE s.hidden = FALSE AND vs.vid IN(!l)
- AND cr.id IN(SELECT cv.cid FROM chars_vns cv WHERE cv.vid = vr.vid AND cv.cid = cr.id)
- ORDER BY cr.name|,
+ $join
+ WHERE s.hidden = FALSE AND vs.$colname IN(!l)
+ ORDER BY c.name",
[ keys %r ]
)});
}
- if($o{what} =~ /anime/) {
- push(@{$r->[$r{$_->{vid}}]{anime}}, $_) && delete $_->{vid} for (@{$self->dbAll(q|
- SELECT va.vid, a.id, a.year, a.ann_id, a.nfo_id, a.type, a.title_romaji, a.title_kanji, extract('epoch' from a.lastfetch) AS lastfetch
- FROM vn_anime va
+ if($what =~ /anime/) {
+ push(@{$r->[$r{ delete $_->{xid} }]{anime}}, $_) for (@{$self->dbAll("
+ SELECT va.$colname AS xid, a.id, a.year, a.ann_id, a.nfo_id, a.type, a.title_romaji, a.title_kanji, extract('epoch' from a.lastfetch) AS lastfetch
+ FROM vn_anime$hist va
JOIN anime a ON va.aid = a.id
- WHERE va.vid IN(!l)|,
+ WHERE va.$colname IN(!l)",
[ keys %r ]
)});
}
- if($o{what} =~ /relations/) {
- push(@{$r->[$r{$_->{vid1}}]{relations}}, {
- relation => $_->{relation},
- official => $_->{official},
- id => $_->{vid2},
- title => $_->{title},
- original => $_->{original},
- }) for(@{$self->dbAll(q|
- SELECT rel.vid1, rel.vid2, rel.relation, rel.official, vr.title, vr.original
- FROM vn_relations rel
- JOIN vn v ON rel.vid2 = v.id
- JOIN vn_rev vr ON v.latest = vr.id
- WHERE rel.vid1 IN(!l)|,
+ if($what =~ /relations/) {
+ push(@{$r->[$r{ delete $_->{xid} }]{relations}}, $_) for(@{$self->dbAll("
+ SELECT rel.$colname AS xid, rel.vid AS id, rel.relation, rel.official, v.title, v.original
+ FROM vn_relations$hist rel
+ JOIN vn v ON rel.vid = v.id
+ WHERE rel.$colname IN(!l)",
[ keys %r ]
)});
}
- if($o{what} =~ /screenshots/) {
- push(@{$r->[$r{$_->{vid}}]{screenshots}}, $_) && delete $_->{vid} for (@{$self->dbAll(q|
- SELECT vs.vid, s.id, vs.nsfw, vs.rid, s.width, s.height
- FROM vn_screenshots vs
+ if($what =~ /screenshots/) {
+ push(@{$r->[$r{ delete $_->{xid} }]{screenshots}}, $_) for (@{$self->dbAll("
+ SELECT vs.$colname AS xid, s.id, vs.nsfw, vs.rid, s.width, s.height
+ FROM vn_screenshots$hist vs
JOIN screenshots s ON vs.scr = s.id
- WHERE vs.vid IN(!l)
- ORDER BY vs.scr|,
+ WHERE vs.$colname IN(!l)
+ ORDER BY vs.scr",
[ keys %r ]
)});
}
@@ -296,41 +310,36 @@ sub dbScreenshotGet {
sub dbScreenshotRandom {
my($self, @vids) = @_;
return $self->dbAll(q|
- SELECT s.id AS scr, s.width, s.height, vr.vid, vr.title
+ SELECT s.id AS scr, s.width, s.height, v.id AS vid, v.title
FROM screenshots s
JOIN vn_screenshots vs ON vs.scr = s.id
- JOIN vn_rev vr ON vr.id = vs.vid
- JOIN vn v ON v.id = vr.vid AND v.latest = vs.vid
+ JOIN vn v ON v.id = vs.id
WHERE NOT v.hidden AND NOT vs.nsfw
AND s.id IN(
SELECT floor(random() * last_value)::integer
- FROM generate_series(1,20), (SELECT last_value FROM screenshots_id_seq) s1
+ FROM generate_series(1,20), (SELECT MAX(id) AS last_value FROM screenshots) s1
LIMIT 20
)
LIMIT 4|
) if !@vids;
# this query is faster than it looks
return $self->dbAll(join(' UNION ALL ', map
- q|SELECT s.id AS scr, s.width, s.height, vr.vid, vr.title, RANDOM() AS position
- FROM vn v
- JOIN vn_rev vr ON vr.id = v.latest
- JOIN vn_screenshots vs ON vs.vid = v.latest
+ q|SELECT s.id AS scr, s.width, s.height, v.id AS vid, v.title, RANDOM() AS position
+ FROM (
+ SELECT vs2.id, vs2.scr FROM vn_screenshots vs2
+ WHERE vs2.id = ? AND NOT vs2.nsfw
+ ORDER BY RANDOM() LIMIT 1
+ ) vs
+ JOIN vn v ON v.id = vs.id
JOIN screenshots s ON s.id = vs.scr
- WHERE v.id = ? AND s.id = (
- SELECT vs2.scr
- FROM vn_screenshots vs2
- JOIN vn v2 ON v2.latest = vs2.vid
- WHERE v2.id = v.id AND NOT vs2.nsfw
- ORDER BY RANDOM()
- LIMIT 1
- )|, @vids).' ORDER BY position', @vids);
+ |, @vids).' ORDER BY position', @vids);
}
sub dbVNHasChar {
my($self, $vid) = @_;
return $self->dbRow(
- 'SELECT 1 AS exists FROM chars c JOIN chars_vns cv ON c.latest = cv.cid WHERE cv.vid = ? AND NOT c.hidden', $vid
+ 'SELECT 1 AS exists FROM chars c JOIN chars_vns cv ON c.id = cv.id WHERE cv.vid = ? AND NOT c.hidden', $vid
)->{exists};
}
@@ -338,7 +347,7 @@ sub dbVNHasChar {
sub dbVNHasStaff {
my($self, $vid) = @_;
return $self->dbRow(
- 'SELECT 1 AS exists FROM vn_staff vs FULL OUTER JOIN vn_seiyuu vsy ON vs.vid = vsy.vid JOIN vn v ON v.latest = vs.vid OR v.latest = vsy.vid WHERE v.id = ?', $vid
+ 'SELECT 1 AS exists FROM vn_staff WHERE id = ? UNION ALL SELECT 1 FROM vn_seiyuu WHERE id = ? LIMIT 1', $vid, $vid
)->{exists};
}
@@ -347,16 +356,12 @@ sub dbVNHasStaff {
sub dbVNImportSeiyuu {
my($self, $vid, $cids) = @_;
return $self->dbAll(q|
- SELECT DISTINCT ON(cr.cid) cr.cid, cr.name AS c_name, s.id AS sid, sa.id AS aid, sa.name
+ SELECT DISTINCT ON(c.id) c.id AS cid, c.name AS c_name, sa.id AS sid, sa.aid, sa.name
FROM vn_seiyuu vs
- JOIN vn v ON v.latest = vs.vid
JOIN chars c ON c.id = vs.cid
- JOIN chars_rev cr ON cr.id = c.latest
- JOIN staff_alias sa ON sa.id = vs.aid
- JOIN staff s ON sa.rid = s.latest
- WHERE vs.cid IN(!l) AND v.id <> ?|, $cids, $vid);
+ JOIN staff_alias sa ON sa.aid = vs.aid
+ WHERE vs.cid IN(!l) AND vs.id <> ?|, $cids, $vid);
}
1;
-
diff --git a/lib/VNDB/Func.pm b/lib/VNDB/Func.pm
index b4848796..2644ea35 100644
--- a/lib/VNDB/Func.pm
+++ b/lib/VNDB/Func.pm
@@ -31,8 +31,8 @@ sub clearfloat {
# Draws a CSS icon, arguments: class, title
sub cssicon {
- acronym class => "icons $_[0]", title => $_[1];
- lit '&nbsp;';
+ abbr class => "icons $_[0]", title => $_[1];
+ lit '&#xa0;';
end;
}
diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm
index 7171d506..98847b4c 100644
--- a/lib/VNDB/Handler/Chars.pm
+++ b/lib/VNDB/Handler/Chars.pm
@@ -20,9 +20,10 @@ TUWF::register(
sub page {
my($self, $id, $rev) = @_;
- my $r = $self->dbCharGet(
+ my $method = $rev ? 'dbCharGetRev' : 'dbCharGet';
+ my $r = $self->$method(
id => $id,
- what => 'extended traits vns seiyuu'.($rev ? ' changes' : ''),
+ what => 'extended traits vns seiyuu',
$rev ? ( rev => $rev ) : ()
)->[0];
return $self->resNotFound if !$r->{id};
@@ -32,7 +33,7 @@ sub page {
return if $self->htmlHiddenMessage('c', $r);
if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbCharGet(id => $id, rev => $rev-1, what => 'changes extended traits vns')->[0];
+ my $prev = $rev && $rev > 1 && $self->dbCharGetRev(id => $id, rev => $rev-1, what => 'extended traits vns')->[0];
$self->htmlRevision('c', $prev, $r,
[ name => diff => 1 ],
[ original => diff => 1 ],
@@ -265,9 +266,9 @@ sub edit {
$copy = $rev && $rev eq 'copy' || $copy && $copy eq 'copy';
$rev = undef if defined $rev && $rev !~ /^\d+$/;
- my $r = $id && $self->dbCharGet(id => $id, what => 'changes extended vns traits', $rev ? (rev => $rev) : ())->[0];
+ my $r = $id && $self->dbCharGetRev(id => $id, what => 'extended vns traits', $rev ? (rev => $rev) : ())->[0];
return $self->resNotFound if $id && !$r->{id};
- $rev = undef if !$r || $r->{cid} == $r->{latest};
+ $rev = undef if !$r || $r->{lastrev};
return $self->htmlDenied if !$self->authCan('edit')
|| $id && (($r->{locked} || $r->{hidden}) && !$self->authCan('dbmod'));
@@ -343,8 +344,8 @@ sub edit {
$_->[1]||=undef for (@vns);
$frm->{vns} = \@vns;
- my $nrev = $self->dbItemEdit(c => !$copy && $id ? $r->{cid} : undef, %$frm);
- return $self->resRedirect("/c$nrev->{iid}.$nrev->{rev}", 'post');
+ my $nrev = $self->dbItemEdit(c => !$copy && $id ? ($r->{id}, $r->{rev}) : (undef, undef), %$frm);
+ return $self->resRedirect("/c$nrev->{itemid}.$nrev->{rev}", 'post');
}
}
diff --git a/lib/VNDB/Handler/Misc.pm b/lib/VNDB/Handler/Misc.pm
index 8176d854..cb9a508a 100644
--- a/lib/VNDB/Handler/Misc.pm
+++ b/lib/VNDB/Handler/Misc.pm
@@ -70,12 +70,12 @@ sub homepage {
a href => '/hist', mt '_home_recentchanges'; txt ' ';
a href => '/feeds/changes.atom'; cssicon 'feed', mt '_atom_feed'; end;
end;
- my $changes = $self->dbRevisionGet(what => 'item user', results => 10, auto => 1);
+ my $changes = $self->dbRevisionGet(results => 10, auto => 1);
ul;
for (@$changes) {
li;
lit mt '_home_recentchanges_item', $_->{type},
- sprintf('<a href="%s" title="%s">%s</a>', "/$_->{type}$_->{iid}.$_->{rev}",
+ sprintf('<a href="%s" title="%s">%s</a>', "/$_->{type}$_->{itemid}.$_->{rev}",
xml_escape($_->{ioriginal}||$_->{ititle}), xml_escape shorten $_->{ititle}, 33),
$_;
end;
@@ -213,8 +213,7 @@ sub history {
# get the edit history
my($list, $np) = $self->dbRevisionGet(
- what => 'item user',
- $type && $type ne 'u' ? ( type => $type, iid => $id ) : (),
+ $type && $type ne 'u' ? ( type => $type, itemid => $id ) : (),
$type eq 'u' ? ( uid => $id ) : (),
$f->{t} ? ( type => $f->{t} eq 'a' ? [qw|v r p|] : $f->{t} ) : (),
page => $f->{p},
diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm
index 6369d4d1..69000b27 100644
--- a/lib/VNDB/Handler/Producers.pm
+++ b/lib/VNDB/Handler/Producers.pm
@@ -44,9 +44,10 @@ sub rg {
sub page {
my($self, $pid, $rev) = @_;
- my $p = $self->dbProducerGet(
+ my $method = $rev ? 'dbProducerGetRev' : 'dbProducerGet';
+ my $p = $self->$method(
id => $pid,
- what => 'extended relations'.($rev ? ' changes' : ''),
+ what => 'extended relations',
$rev ? ( rev => $rev ) : ()
)->[0];
return $self->resNotFound if !$p->{id};
@@ -56,7 +57,7 @@ sub page {
return if $self->htmlHiddenMessage('p', $p);
if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbProducerGet(id => $pid, rev => $rev-1, what => 'changes extended relations')->[0];
+ my $prev = $rev && $rev > 1 && $self->dbProducerGetRev(id => $pid, rev => $rev-1, what => 'extended relations')->[0];
$self->htmlRevision('p', $prev, $p,
[ type => serialize => sub { mt "_ptype_$_[0]" } ],
[ name => diff => 1 ],
@@ -200,9 +201,9 @@ sub _releases {
sub edit {
my($self, $pid, $rev) = @_;
- my $p = $pid && $self->dbProducerGet(id => $pid, what => 'changes extended relations', $rev ? (rev => $rev) : ())->[0];
+ my $p = $pid && $self->dbProducerGetRev(id => $pid, what => 'extended relations', rev => $rev)->[0];
return $self->resNotFound if $pid && !$p->{id};
- $rev = undef if !$p || $p->{cid} == $p->{latest};
+ $rev = undef if !$p || $p->{lastrev};
return $self->htmlDenied if !$self->authCan('edit')
|| $pid && (($p->{locked} || $p->{hidden}) && !$self->authCan('dbmod'));
@@ -246,16 +247,16 @@ sub edit {
$frm->{relations} = $relations;
$frm->{l_wp} = undef if !$frm->{l_wp};
- my $nrev = $self->dbItemEdit(p => $pid ? $p->{cid} : undef, %$frm);
+ my $nrev = $self->dbItemEdit(p => $pid||undef, $pid ? $p->{rev} : undef, %$frm);
# update reverse relations
if(!$pid && $#$relations >= 0 || $pid && $frm->{prodrelations} ne $b4{prodrelations}) {
my %old = $pid ? (map { $_->{id} => $_->{relation} } @{$p->{relations}}) : ();
my %new = map { $_->[1] => $_->[0] } @$relations;
- _updreverse($self, \%old, \%new, $nrev->{iid}, $nrev->{rev});
+ _updreverse($self, \%old, \%new, $nrev->{itemid}, $nrev->{rev});
}
- return $self->resRedirect("/p$nrev->{iid}.$nrev->{rev}", 'post');
+ return $self->resRedirect("/p$nrev->{itemid}.$nrev->{rev}", 'post');
}
}
@@ -329,10 +330,10 @@ sub _updreverse {
# edit all related producers
for my $i (keys %upd) {
- my $r = $self->dbProducerGet(id => $i, what => 'relations')->[0];
+ my $r = $self->dbProducerGetRev(id => $i, what => 'relations')->[0];
my @newrel = map $_->{id} != $pid ? [ $_->{relation}, $_->{id} ] : (), @{$r->{relations}};
push @newrel, [ $upd{$i}, $pid ] if $upd{$i};
- $self->dbItemEdit(p => $r->{cid},
+ $self->dbItemEdit(p => $i, $r->{rev},
relations => \@newrel,
editsum => "Reverse relation update caused by revision p$pid.$rev",
uid => 1,
diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm
index b06a62f3..30505562 100644
--- a/lib/VNDB/Handler/Releases.pm
+++ b/lib/VNDB/Handler/Releases.pm
@@ -20,9 +20,10 @@ TUWF::register(
sub page {
my($self, $rid, $rev) = @_;
- my $r = $self->dbReleaseGet(
+ my $method = $rev ? 'dbReleaseGetRev' : 'dbReleaseGet';
+ my $r = $self->$method(
id => $rid,
- what => 'vn extended producers platforms media'.($rev ? ' changes' : ''),
+ what => 'vn extended producers platforms media',
$rev ? (rev => $rev) : (),
)->[0];
return $self->resNotFound if !$r->{id};
@@ -32,7 +33,7 @@ sub page {
return if $self->htmlHiddenMessage('r', $r);
if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbReleaseGet(
+ my $prev = $rev && $rev > 1 && $self->dbReleaseGetRev(
id => $rid, rev => $rev-1,
what => 'vn extended producers platforms media changes'
)->[0];
@@ -271,9 +272,9 @@ sub edit {
$rid = 0;
}
- my $r = $rid && $self->dbReleaseGet(id => $rid, what => 'vn extended producers platforms media changes', $rev ? (rev => $rev) : ())->[0];
+ my $r = $rid && $self->dbReleaseGetRev(id => $rid, what => 'vn extended producers platforms media', $rev ? (rev => $rev) : ())->[0];
return $self->resNotFound if $rid && !$r->{id};
- $rev = undef if !$r || $r->{cid} == $r->{latest};
+ $rev = undef if !$r || $r->{lastrev};
my $v = $vid && $self->dbVNGet(id => $vid)->[0];
return $self->resNotFound if $vid && !$v->{id};
@@ -349,7 +350,7 @@ sub edit {
}
if(!$frm->{_err}) {
- my $nrev = $self->dbItemEdit(r => !$copy && $rid ? $r->{cid} : undef,
+ my $nrev = $self->dbItemEdit(r => !$copy && $rid ? ($r->{id}, $r->{rev}) : (undef, undef),
(map { $_ => $frm->{$_} } qw| type title original gtin catalog languages website released minage
notes platforms resolution editsum patch voiced freeware doujin ani_story ani_ero ihid ilock|),
vn => $new_vn,
@@ -357,7 +358,7 @@ sub edit {
media => $media,
);
- return $self->resRedirect("/r$nrev->{iid}.$nrev->{rev}", 'post');
+ return $self->resRedirect("/r$nrev->{itemid}.$nrev->{rev}", 'post');
}
}
@@ -388,8 +389,8 @@ sub _form {
[ check => short => 'patch', name => mt('_redit_form_patch') ],
[ check => short => 'freeware', name => mt('_redit_form_freeware') ],
[ check => short => 'doujin', name => mt('_redit_form_doujin') ],
- [ input => short => 'title', name => mt('_redit_form_title'), width => 300 ],
- [ input => short => 'original', name => mt('_redit_form_original'), width => 300 ],
+ [ input => short => 'title', name => mt('_redit_form_title'), width => 450 ],
+ [ input => short => 'original', name => mt('_redit_form_original'), width => 450 ],
[ static => content => mt '_redit_form_original_note' ],
[ select => short => 'languages', name => mt('_redit_form_languages'), multi => 1,
options => [ map [ $_, "$_ (".mt("_lang_$_").')' ], sort @{$self->{languages}} ] ],
diff --git a/lib/VNDB/Handler/Staff.pm b/lib/VNDB/Handler/Staff.pm
index 88e6d7c8..0f878ca6 100644
--- a/lib/VNDB/Handler/Staff.pm
+++ b/lib/VNDB/Handler/Staff.pm
@@ -19,9 +19,10 @@ TUWF::register(
sub page {
my($self, $id, $rev) = @_;
- my $s = $self->dbStaffGet(
+ my $method = $rev ? 'dbStaffGetRev' : 'dbStaffGet';
+ my $s = $self->$method(
id => $id,
- what => 'extended aliases roles'.($rev ? ' changes' : ''),
+ what => 'extended aliases roles',
$rev ? ( rev => $rev ) : ()
)->[0];
return $self->resNotFound if !$s->{id};
@@ -31,7 +32,7 @@ sub page {
return if $self->htmlHiddenMessage('s', $s);
if($rev) {
- my $prev = $rev && $rev > 1 && $self->dbStaffGet(id => $id, rev => $rev-1, what => 'changes extended aliases')->[0];
+ my $prev = $rev && $rev > 1 && $self->dbStaffGetRev(id => $id, rev => $rev-1, what => 'extended aliases')->[0];
$self->htmlRevision('s', $prev, $s,
[ name => diff => 1 ],
[ original => diff => 1 ],
@@ -179,18 +180,18 @@ sub _cast {
sub edit {
my($self, $sid, $rev) = @_;
- my $s = $sid && $self->dbStaffGet(id => $sid, what => 'changes extended aliases', $rev ? (rev => $rev) : ())->[0];
+ my $s = $sid && $self->dbStaffGetRev(id => $sid, what => 'extended aliases roles', $rev ? (rev => $rev) : ())->[0];
return $self->resNotFound if $sid && !$s->{id};
- $rev = undef if !$s || $s->{cid} == $s->{latest};
+ $rev = undef if !$s || $s->{lastrev};
- return $self->htmlDenied if !$self->authCan('staffedit')
+ return $self->htmlDenied if !$self->authCan('edit')
|| $sid && (($s->{locked} || $s->{hidden}) && !$self->authCan('dbmod'));
my %b4 = !$sid ? () : (
(map { $_ => $s->{$_} } qw|name original gender lang desc l_wp l_site l_twitter l_anidb ihid ilock|),
primary => $s->{aid},
aliases => [
- map +{ aid => $_->{id}, name => $_->{name}, orig => $_->{original} },
+ map +{ aid => $_->{aid}, name => $_->{name}, orig => $_->{original} },
sort { $a->{name} cmp $b->{name} || $a->{original} cmp $b->{original} } @{$s->{aliases}}
],
);
@@ -220,20 +221,26 @@ sub edit {
);
if(!$frm->{_err}) {
- my %old_aliases = $sid ? ( map +($_->{id} => 1), @{$self->dbStaffAliasIds($sid)} ) : ();
+ my %old_aliases = $sid ? ( map +($_->{aid} => 1), @{$self->dbStaffAliasIds($sid)} ) : ();
$frm->{primary} = 0 unless exists $old_aliases{$frm->{primary}};
# reset aid to zero for newly added aliases.
$_->{aid} *= $old_aliases{$_->{aid}} ? 1 : 0 for(@{$frm->{aliases}});
+ # Make sure no aliases that have been linked to a VN are removed.
+ my %new_aliases = map +($_, 1), grep $_, $frm->{primary}, map $_->{aid}, @{$frm->{aliases}};
+ $frm->{_err} = [ 'usedalias' ] if grep !$new_aliases{$_->{aid}}, @{$s->{roles}}, @{$self->{cast}};
+ }
+
+ if(!$frm->{_err}) {
$frm->{ihid} = $frm->{ihid} ?1:0;
$frm->{ilock} = $frm->{ilock}?1:0;
$frm->{aid} = $frm->{primary} if $sid;
$frm->{desc} = $self->bbSubstLinks($frm->{desc});
return $self->resRedirect("/s$sid", 'post') if $sid && !form_compare(\%b4, $frm);
- my $nrev = $self->dbItemEdit ('s' => $sid ? $s->{cid} : undef, %$frm);
- return $self->resRedirect("/s$nrev->{iid}.$nrev->{rev}", 'post');
+ my $nrev = $self->dbItemEdit(s => $sid ? ($s->{id}, $s->{rev}) : (undef, undef), %$frm);
+ return $self->resRedirect("/s$nrev->{itemid}.$nrev->{rev}", 'post');
}
}
@@ -384,4 +391,3 @@ sub staffxml {
}
1;
-__END__
diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm
index 491c573b..6b30db7b 100644
--- a/lib/VNDB/Handler/Users.pm
+++ b/lib/VNDB/Handler/Users.pm
@@ -33,6 +33,7 @@ sub userpage {
return $self->resNotFound if !$u->{id};
my $votes = $u->{c_votes} && $self->dbVoteStats(uid => $uid);
+ my $list_visible = !$u->{hide_list} || ($self->authInfo->{id}||0) == $u->{id} || $self->authCan('usermod');
my $title = mt '_userpage_title', $u->{username};
$self->htmlHeader(title => $title, noindex => 1);
@@ -70,7 +71,7 @@ sub userpage {
Tr;
td mt '_userpage_votes';
td;
- if($u->{hide_list}) {
+ if(!$list_visible) {
txt mt '_userpage_hidden';
} elsif($votes) {
my($total, $count) = (0, 0);
@@ -100,7 +101,7 @@ sub userpage {
Tr;
td mt '_userpage_list';
- td $u->{hide_list} ? mt('_userpage_hidden') :
+ td !$list_visible ? mt('_userpage_hidden') :
mt('_userpage_list_item', $u->{releasecount}, $u->{vncount});
end;
@@ -117,7 +118,7 @@ sub userpage {
end 'table';
end 'div';
- if(!$u->{hide_list} && $votes) {
+ if($votes && $list_visible) {
div class => 'mainbox';
h1 mt '_userpage_votestats';
$self->htmlVoteStats(u => $u, $votes);
@@ -125,7 +126,7 @@ sub userpage {
}
if($u->{c_changes}) {
- my $list = $self->dbRevisionGet(what => 'item user', uid => $uid, results => 5);
+ my $list = $self->dbRevisionGet(uid => $uid, results => 5);
h1 class => 'boxtitle';
a href => "/u$uid/hist", mt '_userpage_changes';
end;
diff --git a/lib/VNDB/Handler/VNEdit.pm b/lib/VNDB/Handler/VNEdit.pm
index 8869d1a9..2d67a3df 100644
--- a/lib/VNDB/Handler/VNEdit.pm
+++ b/lib/VNDB/Handler/VNEdit.pm
@@ -64,8 +64,8 @@ sub addform {
$self->htmlForm({ frm => $frm, action => '/v/add', continue => @$l ? 2 : 1 },
vn_add => [ mt('_vnedit_title_add'),
- [ input => short => 'title', name => mt '_vnedit_frm_title' ],
- [ input => short => 'original', name => mt '_vnedit_original' ],
+ [ input => short => 'title', name => mt('_vnedit_frm_title'), width => 450 ],
+ [ input => short => 'original', name => mt('_vnedit_original'), width => 450 ],
[ static => content => mt '_vnedit_original_msg' ],
[ textarea => short => 'alias', name => mt('_vnedit_alias'), rows => 4 ],
[ static => content => mt '_vnedit_alias_msg' ],
@@ -77,9 +77,9 @@ sub addform {
sub edit {
my($self, $vid, $rev, $nosubmit) = @_;
- my $v = $vid && $self->dbVNGet(id => $vid, what => 'extended screenshots relations anime credits changes', $rev ? (rev => $rev) : ())->[0];
+ my $v = $vid && $self->dbVNGetRev(id => $vid, what => 'extended screenshots relations anime credits changes', $rev ? (rev => $rev) : ())->[0];
return $self->resNotFound if $vid && !$v->{id};
- $rev = undef if !$vid || $v->{cid} == $v->{latest};
+ $rev = undef if !$vid || $v->{lastrev};
return $self->htmlDenied if !$self->authCan('edit')
|| $vid && (($v->{locked} || $v->{hidden}) && !$self->authCan('dbmod'));
@@ -176,7 +176,7 @@ sub edit {
return $self->resRedirect("/v$vid", 'post') if $vid && !form_compare(\%b4, $frm);
# perform the edit/add
- my $nrev = $self->dbItemEdit(v => $vid ? $v->{cid} : undef,
+ my $nrev = $self->dbItemEdit(v => $vid ? ($v->{id}, $v->{rev}) : (undef, undef),
(map { $_ => $frm->{$_} } qw|title original image alias desc length l_wp l_encubed l_renai editsum img_nsfw ihid ilock credits seiyuu screenshots|),
anime => [ keys %$anime ],
relations => $relations,
@@ -186,10 +186,10 @@ sub edit {
if(!$vid && $#$relations >= 0 || $vid && $frm->{vnrelations} ne $b4{vnrelations}) {
my %old = $vid ? (map +($_->{id} => [ $_->{relation}, $_->{official} ]), @{$v->{relations}}) : ();
my %new = map +($_->[1] => [ $_->[0], $_->[2] ]), @$relations;
- _updreverse($self, \%old, \%new, $nrev->{iid}, $nrev->{rev});
+ _updreverse($self, \%old, \%new, $nrev->{itemid}, $nrev->{rev});
}
- return $self->resRedirect("/v$nrev->{iid}.$nrev->{rev}", 'post');
+ return $self->resRedirect("/v$nrev->{itemid}.$nrev->{rev}", 'post');
}
}
@@ -250,8 +250,8 @@ sub _form {
my $import = @$chars ? $self->dbVNImportSeiyuu($v->{id}, [ map $_->{id}, @$chars ]) : [];
$self->htmlForm({ frm => $frm, action => $v ? "/v$v->{id}/edit" : '/v/new', editsum => 1, upload => 1 },
vn_geninfo => [ mt('_vnedit_geninfo'),
- [ input => short => 'title', name => mt '_vnedit_frm_title' ],
- [ input => short => 'original', name => mt '_vnedit_original' ],
+ [ input => short => 'title', name => mt('_vnedit_frm_title'), width => 450 ],
+ [ input => short => 'original', name => mt('_vnedit_original'), width => 450 ],
[ static => content => mt '_vnedit_original_msg' ],
[ textarea => short => 'alias', name => mt('_vnedit_alias'), rows => 4 ],
[ static => content => mt '_vnedit_alias_msg' ],
@@ -422,7 +422,7 @@ sub _form {
# Update reverse relations and regenerate relation graph
# Arguments: %old. %new, vid, rev
-# %old,%new -> { vid2 => [ relation, official ], .. }
+# %old,%new -> { vid => [ relation, official ], .. }
# from the perspective of vid
# rev is of the related edit
sub _updreverse {
@@ -441,10 +441,10 @@ sub _updreverse {
# edit all related VNs
for my $i (keys %upd) {
- my $r = $self->dbVNGet(id => $i, what => 'relations')->[0];
+ my $r = $self->dbVNGetRev(id => $i, what => 'relations')->[0];
my @newrel = map $_->{id} != $vid ? [ $_->{relation}, $_->{id}, $_->{official} ] : (), @{$r->{relations}};
push @newrel, [ $upd{$i}[0], $vid, $upd{$i}[1] ] if $upd{$i};
- $self->dbItemEdit(v => $r->{cid},
+ $self->dbItemEdit(v => $r->{id}, $r->{rev},
relations => \@newrel,
editsum => "Reverse relation update caused by revision v$vid.$rev",
uid => 1, # Multi
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm
index 9c85f922..02d14139 100644
--- a/lib/VNDB/Handler/VNPage.pm
+++ b/lib/VNDB/Handler/VNPage.pm
@@ -187,8 +187,7 @@ my @rel_cols = (
sub releases {
my($self, $vid) = @_;
- my $v = $self->dbVNGet(
- id => $vid)->[0];
+ my $v = $self->dbVNGet(id => $vid)->[0];
return $self->resNotFound if !$v->{id};
my $title = mt('_vnpage_rel_title', $v->{title});
@@ -334,9 +333,10 @@ sub page {
my $staff = $rev && $rev eq 'staff';
$rev = undef if $char || $staff;
- my $v = $self->dbVNGet(
+ my $method = $rev ? 'dbVNGetRev' : 'dbVNGet';
+ my $v = $self->$method(
id => $vid,
- what => 'extended anime relations screenshots rating ranking'.($staff || $rev ? ' credits' : '').($rev ? ' changes' : ''),
+ what => 'extended anime relations screenshots rating ranking'.($staff || $rev ? ' credits' : ''),
$rev ? (rev => $rev) : (),
)->[0];
return $self->resNotFound if !$v->{id};
@@ -508,8 +508,8 @@ sub _revision {
my($self, $v, $rev) = @_;
return if !$rev;
- my $prev = $rev && $rev > 1 && $self->dbVNGet(
- id => $v->{id}, rev => $rev-1, what => 'extended anime relations screenshots credits changes'
+ my $prev = $rev && $rev > 1 && $self->dbVNGetRev(
+ id => $v->{id}, rev => $rev-1, what => 'extended anime relations screenshots credits'
)->[0];
$self->htmlRevision('v', $prev, $v,
@@ -554,7 +554,7 @@ sub _revision {
return @r ? @r : (mt '_revision_empty');
}],
[ screenshots => join => '<br />', split => sub {
- my @r = map sprintf('[%s] <a href="%s" rel="iv:%dx%d">%d</a> (%s)',
+ my @r = map sprintf('[%s] <a href="%s" data-iv="%dx%d">%d</a> (%s)',
$_->{rid} ? qq|<a href="/r$_->{rid}">r$_->{rid}</a>| : 'no release',
imgurl(sf => $_->{id}), $_->{width}, $_->{height}, $_->{id},
mt($_->{nsfw} ? '_vndiff_nsfw_notsafe' : '_vndiff_nsfw_safe')
@@ -668,7 +668,7 @@ sub _anime {
}
txt '] ';
end;
- acronym title => $_->{title_kanji}||$_->{title_romaji}, shorten $_->{title_romaji}, 50;
+ abbr title => $_->{title_kanji}||$_->{title_romaji}, shorten $_->{title_romaji}, 50;
b ' ('.(defined $_->{type} ? mt("_animetype_$_->{type}").', ' : '').$_->{year}.')';
br;
}
@@ -748,7 +748,7 @@ sub _affiliate_links {
|| ($f->{default_version} && $f->{default_version}->($self, $link, $rel))
|| $version;
txt " at $f->{name}";
- acronym class => 'pricenote', title =>
+ abbr class => 'pricenote', title =>
$link->{lastfetch} ? sprintf('Last updated: %s.', $en->age($link->{lastfetch})) : '', " for $link->{price}"
if $link->{price};
txt ' »';
@@ -859,7 +859,7 @@ sub _screenshots {
my($w, $h) = imgsize($_->{width}, $_->{height}, @{$self->{scr_size}});
a href => imgurl(sf => $_->{id}),
class => sprintf('scrlnk%s%s', $_->{nsfw} ? ' nsfw':'', $_->{nsfw}&&!$self->authPref('show_nsfw')?' hidden':''),
- rel => "iv:$_->{width}x$_->{height}:scr";
+ 'data-iv' => "$_->{width}x$_->{height}:scr";
img src => imgurl(st => $_->{id}),
width => $w, height => $h, alt => mt '_vnpage_scr_num', $_->{id};
end;
diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm
index a1c6cf08..0c3b5b73 100644
--- a/lib/VNDB/Util/Auth.pm
+++ b/lib/VNDB/Util/Auth.pm
@@ -5,7 +5,7 @@ package VNDB::Util::Auth;
use strict;
use warnings;
use Exporter 'import';
-use Digest::SHA qw|sha1 sha1_hex sha256|;
+use Digest::SHA qw|sha1 sha1_hex|;
use Crypt::URandom 'urandom';
use Crypt::ScryptKDF 'scrypt_raw';
use Encode 'encode_utf8';
@@ -112,16 +112,7 @@ sub _authCheck {
my $d = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0];
return 0 if !$d->{id};
- # Old-style hashes
- if(length $d->{passwd} == 41) {
- return 0 if _authPreparePassSha256($self, $pass, substr $d->{passwd}, 0, 9) ne $d->{passwd};
- $self->{_auth} = $d;
- # Update database with new hash format, now that we have the plain text password
- $self->dbUserEdit($d->{id}, passwd => $self->authPreparePass($pass));
- return 1;
- }
-
- # New scrypt hashes
+ # scrypt format
if(length $d->{passwd} == 46) {
my($N, $r, $p, $salt) = unpack 'NCCa8', $d->{passwd};
return 0 if $self->authPreparePass($pass, $salt, $N, $r, $p) ne $d->{passwd};
@@ -144,16 +135,6 @@ sub authPreparePass {
}
-# Same as authPreparePass, but for the old sha256 hash.
-# Arguments: pass, optionally salt
-# Returns: encrypted password (as a binary string)
-sub _authPreparePassSha256 {
- my($self, $pass, $salt) = @_;
- $salt ||= encode_utf8(randomascii(9));
- return $salt.sha256($self->{global_salt} . encode_utf8($pass) . $salt);
-}
-
-
# Generates a random token that can be used to reset the password.
# Returns: token (hex string), token-encrypted (binary string)
sub authPrepareReset {
diff --git a/lib/VNDB/Util/BrowseHTML.pm b/lib/VNDB/Util/BrowseHTML.pm
index b54ad5d9..d57c0546 100644
--- a/lib/VNDB/Util/BrowseHTML.pm
+++ b/lib/VNDB/Util/BrowseHTML.pm
@@ -137,11 +137,11 @@ sub htmlBrowseHist {
],
row => sub {
my($s, $n, $i) = @_;
- my $revurl = "/$i->{type}$i->{iid}.$i->{rev}";
+ my $revurl = "/$i->{type}$i->{itemid}.$i->{rev}";
Tr;
td class => 'tc1_1';
- a href => $revurl, "$i->{type}$i->{iid}";
+ a href => $revurl, "$i->{type}$i->{itemid}";
end;
td class => 'tc1_2';
a href => $revurl, ".$i->{rev}";
diff --git a/lib/VNDB/Util/CommonHTML.pm b/lib/VNDB/Util/CommonHTML.pm
index 7b3fa673..b576e4ad 100644
--- a/lib/VNDB/Util/CommonHTML.pm
+++ b/lib/VNDB/Util/CommonHTML.pm
@@ -133,12 +133,12 @@ sub htmlHiddenMessage {
my($self, $type, $obj) = @_;
return 0 if !$obj->{hidden};
my $board = $type =~ /[cs]/ ? 'db' : $type eq 'r' ? 'v'.$obj->{vn}[0]{vid} : $type.$obj->{id};
- # fetch edit summary (not present in $obj because the changes aren't fetched)
- my $editsum = $type eq 'v' ? $self->dbVNGet(id => $obj->{id}, what => 'changes')->[0]{comments}
- : $type eq 'r' ? $self->dbReleaseGet(id => $obj->{id}, what => 'changes')->[0]{comments}
- : $type eq 'c' ? $self->dbCharGet(id => $obj->{id}, what => 'changes')->[0]{comments}
- : $type eq 's' ? $self->dbStaffGet(id => $obj->{id}, what => 'changes')->[0]{comments}
- : $self->dbProducerGet(id => $obj->{id}, what => 'changes')->[0]{comments};
+ # fetch edit summary (not present in $obj, requires the db*GetRev() methods)
+ my $editsum = $type eq 'v' ? $self->dbVNGetRev(id => $obj->{id})->[0]{comments}
+ : $type eq 'r' ? $self->dbReleaseGetRev(id => $obj->{id})->[0]{comments}
+ : $type eq 'c' ? $self->dbCharGetRev(id => $obj->{id})->[0]{comments}
+ : $type eq 's' ? $self->dbStaffGetRev(id => $obj->{id})->[0]{comments}
+ : $self->dbProducerGetRev(id => $obj->{id})->[0]{comments};
div class => 'mainbox';
h1 $obj->{title}||$obj->{name};
div class => 'warning';
@@ -183,7 +183,7 @@ sub htmlRevision {
a class => 'prev', href => sprintf('/%s%d.%d', $type, $new->{id}, $new->{rev}-1), '<- '.mt '_revision_previous'
if $new->{rev} > 1;
a class => 'next', href => sprintf('/%s%d.%d', $type, $new->{id}, $new->{rev}+1), mt('_revision_next').' ->'
- if $new->{cid} != $new->{latest};
+ if !$new->{lastrev};
p class => 'center';
a href => "/$type$new->{id}", "$type$new->{id}";
end;
@@ -204,12 +204,12 @@ sub htmlRevision {
table class => 'stripe';
thead;
Tr;
- td; lit '&nbsp;'; end;
+ td; lit '&#xa0;'; end;
td; revheader($self, $type, $old); end;
td; revheader($self, $type, $new); end;
end;
Tr;
- td; lit '&nbsp;'; end;
+ td; lit '&#xa0;'; end;
td colspan => 2;
b mt '_revision_edit_summary', $new->{rev};
br; br;
@@ -310,7 +310,7 @@ sub htmlEditMessage {
}
end;
end;
- if($obj && $obj->{latest} != $obj->{cid}) {
+ if($obj && !$obj->{lastrev}) {
div class => 'warning';
h2 mt '_editmsg_revert_title';
p mt '_editmsg_revert_msg', $num;
@@ -329,11 +329,7 @@ sub htmlItemMessage {
if($obj->{locked}) {
p class => 'locked', mt '_itemmsg_locked';
- } elsif(!$self->authInfo->{id}) {
- p class => 'locked';
- lit mt '_itemmsg_login', '/u/login';
- end;
- } elsif(!$self->authCan('edit')) {
+ } elsif($self->authInfo->{id} && !$self->authCan('edit')) {
p class => 'locked', mt '_itemmsg_denied';
}
}
@@ -439,6 +435,7 @@ sub htmlSearchBox {
sub htmlRGHeader {
my($self, $title, $type, $obj) = @_;
+ # This used to be a good test for inline SVG support, but I'm not sure it is nowadays.
if(($self->reqHeader('Accept')||'') !~ /application\/xhtml\+xml/) {
$self->htmlHeader(title => $title);
$self->htmlMainTabs($type, $obj, 'rg');
@@ -452,8 +449,7 @@ sub htmlRGHeader {
$self->htmlFooter;
return 1;
}
- $self->resHeader('Content-Type' => 'application/xhtml+xml; charset=UTF-8');
- $self->htmlHeader(title => $title, svg => 1);
+ $self->htmlHeader(title => $title);
$self->htmlMainTabs($type, $obj, 'rg');
return 0;
}
diff --git a/lib/VNDB/Util/FormHTML.pm b/lib/VNDB/Util/FormHTML.pm
index 22eeb4c5..e1f3bdfc 100644
--- a/lib/VNDB/Util/FormHTML.pm
+++ b/lib/VNDB/Util/FormHTML.pm
@@ -99,7 +99,7 @@ sub htmlFormPart {
if(/check/) {
Tr class => 'newfield';
td class => 'label';
- lit '&nbsp;';
+ lit '&#xa0;';
end;
td class => 'field';
input type => 'checkbox', name => $o{short}, id => $o{short}, tabindex => 10,
@@ -122,7 +122,7 @@ sub htmlFormPart {
} elsif($o{label}) {
txt $o{label};
} else {
- lit '&nbsp;';
+ lit '&#xa0;';
}
end;
}
diff --git a/lib/VNDB/Util/LayoutHTML.pm b/lib/VNDB/Util/LayoutHTML.pm
index e041960a..f2344fc5 100644
--- a/lib/VNDB/Util/LayoutHTML.pm
+++ b/lib/VNDB/Util/LayoutHTML.pm
@@ -17,11 +17,8 @@ sub htmlHeader { # %options->{ title, noindex, search, feeds, svg }
$skin = $self->{skin_default} if !$self->{skins}{$skin} || !-d "$VNDB::ROOT/static/s/$skin";
# heading
- html lang => $self->{l10n}->language_tag(), $o{svg} ? (
- doctype => 'xhtml-math-svg',
- 'xmlns:svg' => 'http://www.w3.org/2000/svg',
- 'xmlns:xlink' => 'http://www.w3.org/1999/xlink'
- ) : ();
+ lit '<!DOCTYPE HTML>';
+ tag 'html', lang => $self->{l10n}->language_tag();
head;
title $o{title};
Link rel => 'shortcut icon', href => '/favicon.ico', type => 'image/x-icon';
@@ -79,7 +76,7 @@ sub _menu {
form action => '/v/all', method => 'get', id => 'search';
fieldset;
legend 'Search';
- input type => 'text', class => 'text', id => 'sq', name => 'sq', value => $o{search}||mt('_menu_emptysearch');
+ input type => 'text', class => 'text', id => 'sq', name => 'sq', value => $o{search}||'', placeholder => mt('_menu_emptysearch');
input type => 'submit', class => 'submit', value => 'Search';
end;
end;
@@ -104,9 +101,7 @@ sub _menu {
if($self->authCan('edit')) {
a href => '/v/add', mt '_menu_addvn'; br;
a href => '/p/new', mt '_menu_addproducer'; br;
- if($self->authCan('staffedit')) {
- a href => '/s/new', mt '_menu_addstaff'; br;
- }
+ a href => '/s/new', mt '_menu_addstaff'; br;
a href => '/c/new', mt '_menu_addcharacter'; br;
}
br;
diff --git a/util/sql/all.sql b/util/sql/all.sql
index 496479a2..b2b4fabc 100644
--- a/util/sql/all.sql
+++ b/util/sql/all.sql
@@ -9,7 +9,7 @@ CREATE TYPE board_type AS ENUM ('an', 'db', 'ge', 'v', 'p', 'u');
CREATE TYPE char_role AS ENUM ('main', 'primary', 'side', 'appears');
CREATE TYPE credit_type AS ENUM ('scenario', 'chardesign', 'art', 'music', 'songs', 'director', 'staff');
CREATE TYPE dbentry_type AS ENUM ('v', 'r', 'p', 'c', 's');
-CREATE TYPE edit_rettype AS (iid integer, cid integer, rev integer);
+CREATE TYPE edit_rettype AS (itemid integer, chid integer, rev integer);
CREATE TYPE gender AS ENUM ('unknown', 'm', 'f', 'b');
CREATE TYPE language AS ENUM ('ar', 'ca', 'cs', 'da', 'de', 'en', 'es', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'ko', 'nl', 'no', 'pl', 'pt-pt', 'pt-br', 'ro', 'ru', 'sk', 'sv', 'tr', 'uk', 'vi', 'zh');
CREATE TYPE medium AS ENUM ('cd', 'dvd', 'gdr', 'blr', 'flp', 'mrt', 'mem', 'umd', 'nod', 'in', 'otc');
@@ -33,6 +33,10 @@ CREATE TYPE vn_relation AS ENUM ('seq', 'preq', 'set', 'alt', 'char', 'sid
\i util/sql/func.sql
+-- auto-generated editing functions
+
+\i util/sql/editfunc.sql
+
-- constraints & indices
\i util/sql/tableattrs.sql
diff --git a/util/sql/func.sql b/util/sql/func.sql
index 51711f77..125455a2 100644
--- a/util/sql/func.sql
+++ b/util/sql/func.sql
@@ -15,6 +15,14 @@ CREATE OR REPLACE FUNCTION strip_bb_tags(t text) RETURNS text AS $$
SELECT regexp_replace(t, '\[(?:url=[^\]]+|/?(?:spoiler|quote|raw|code|url))\]', ' ', 'gi');
$$ LANGUAGE sql IMMUTABLE;
+-- Wrapper around to_tsvector() and strip_bb_tags(), implemented in plpgsql and
+-- with an associated cost function to make it opaque to the query planner and
+-- ensure the query planner realizes that this function is _slow_.
+CREATE OR REPLACE FUNCTION bb_tsvector(t text) RETURNS tsvector AS $$
+BEGIN
+ RETURN to_tsvector('english', strip_bb_tags(t));
+END;
+$$ LANGUAGE plpgsql IMMUTABLE COST 500;
-- BUG: Since this isn't a full bbcode parser, [spoiler] tags inside [raw] or [code] are still considered spoilers.
CREATE OR REPLACE FUNCTION strip_spoilers(t text) RETURNS text AS $$
@@ -23,60 +31,56 @@ CREATE OR REPLACE FUNCTION strip_spoilers(t text) RETURNS text AS $$
$$ LANGUAGE sql IMMUTABLE;
--- update_vncache(id) - updates the c_* columns in the vn table
+-- update_vncache(id) - updates some c_* columns in the vn table
CREATE OR REPLACE FUNCTION update_vncache(integer) RETURNS void AS $$
UPDATE vn SET
- c_released = COALESCE((SELECT
- MIN(rr1.released)
- FROM releases_rev rr1
- JOIN releases r1 ON rr1.id = r1.latest
- JOIN releases_vn rv1 ON rr1.id = rv1.rid
- WHERE rv1.vid = vn.id
- AND rr1.type <> 'trial'
- AND r1.hidden = FALSE
- AND rr1.released <> 0
- GROUP BY rv1.vid
+ c_released = COALESCE((
+ SELECT MIN(r.released)
+ FROM releases r
+ JOIN releases_vn rv ON r.id = rv.id
+ WHERE rv.vid = $1
+ AND r.type <> 'trial'
+ AND r.hidden = FALSE
+ AND r.released <> 0
+ GROUP BY rv.vid
), 0),
c_olang = ARRAY(
SELECT lang
FROM releases_lang
- WHERE rid = (
- SELECT r.latest
+ WHERE id = (
+ SELECT r.id
FROM releases_vn rv
- JOIN releases r ON rv.rid = r.latest
- JOIN releases_rev rr ON rr.id = r.latest AND rr.rid = r.id
- WHERE rr.released > 0
+ JOIN releases r ON rv.id = r.id
+ WHERE r.released > 0
AND NOT r.hidden
AND rv.vid = $1
- ORDER BY rr.released
+ ORDER BY r.released
LIMIT 1
)
),
c_languages = ARRAY(
- SELECT rl2.lang
- FROM releases_rev rr2
- JOIN releases_lang rl2 ON rl2.rid = rr2.id
- JOIN releases r2 ON rr2.id = r2.latest
- JOIN releases_vn rv2 ON rr2.id = rv2.rid
- WHERE rv2.vid = vn.id
- AND rr2.type <> 'trial'
- AND rr2.released <= TO_CHAR('today'::timestamp, 'YYYYMMDD')::integer
- AND r2.hidden = FALSE
- GROUP BY rl2.lang
- ORDER BY rl2.lang
+ SELECT rl.lang
+ FROM releases_lang rl
+ JOIN releases r ON r.id = rl.id
+ JOIN releases_vn rv ON r.id = rv.id
+ WHERE rv.vid = $1
+ AND r.type <> 'trial'
+ AND r.released <= TO_CHAR('today'::timestamp, 'YYYYMMDD')::integer
+ AND r.hidden = FALSE
+ GROUP BY rl.lang
+ ORDER BY rl.lang
),
c_platforms = ARRAY(
- SELECT rp3.platform
- FROM releases_platforms rp3
- JOIN releases_rev rr3 ON rp3.rid = rr3.id
- JOIN releases r3 ON rp3.rid = r3.latest
- JOIN releases_vn rv3 ON rp3.rid = rv3.rid
- WHERE rv3.vid = vn.id
- AND rr3.type <> 'trial'
- AND rr3.released <= TO_CHAR('today'::timestamp, 'YYYYMMDD')::integer
- AND r3.hidden = FALSE
- GROUP BY rp3.platform
- ORDER BY rp3.platform
+ SELECT rp.platform
+ FROM releases_platforms rp
+ JOIN releases r ON rp.id = r.id
+ JOIN releases_vn rv ON rp.id = rv.id
+ WHERE rv.vid = $1
+ AND r.type <> 'trial'
+ AND r.released <= TO_CHAR('today'::timestamp, 'YYYYMMDD')::integer
+ AND r.hidden = FALSE
+ GROUP BY rp.platform
+ ORDER BY rp.platform
)
WHERE id = $1;
$$ LANGUAGE sql;
@@ -154,9 +158,9 @@ BEGIN
-- all char<->trait links of the latest revisions, including chars inherited from child traits
-- (also includes meta traits, because they could have a normal trait as parent)
WITH RECURSIVE traits_chars_all(lvl, tid, cid, spoiler, meta) AS (
- SELECT 15, tid, c.id, spoil, false
+ SELECT 15, tid, ct.id, spoil, false
FROM chars_traits ct
- JOIN chars c ON c.latest = ct.cid
+ JOIN chars c ON c.id = ct.id
WHERE NOT c.hidden
UNION ALL
SELECT lvl-1, tp.parent, tc.cid, tc.spoiler, t.meta
@@ -186,18 +190,18 @@ $$ LANGUAGE plpgsql;
-- revision insertion abstraction --
----------------------------------------------------------
+-- The two functions below are utility functions used by the item-specific functions in editfunc.sql
--- IMPORTANT: these functions will need to be updated on each change in the DB structure
--- of the relevant tables
-
-
--- create temporary table for generic revision info
-CREATE OR REPLACE FUNCTION edit_revtable(t dbentry_type, i integer) RETURNS void AS $$
+-- create temporary table for generic revision info, and returns the chid of the revision being edited (or NULL).
+CREATE OR REPLACE FUNCTION edit_revtable(xtype dbentry_type, xitemid integer, xrev integer) RETURNS integer AS $$
+DECLARE
+ ret integer;
+ x record;
BEGIN
BEGIN
CREATE TEMPORARY TABLE edit_revision (
type dbentry_type NOT NULL,
- iid integer,
+ itemid integer,
requester integer,
ip inet,
comments text,
@@ -207,15 +211,9 @@ BEGIN
EXCEPTION WHEN duplicate_table THEN
TRUNCATE edit_revision;
END;
- INSERT INTO edit_revision (type, iid, ihid, ilock) VALUES (t,
- ( SELECT vid FROM vn_rev WHERE id = i
- UNION SELECT rid FROM releases_rev WHERE id = i
- UNION SELECT cid FROM chars_rev WHERE id = i
- UNION SELECT pid FROM producers_rev WHERE id = i
- UNION SELECT sid FROM staff_rev WHERE id = i),
- COALESCE((SELECT ihid FROM changes WHERE id = i), FALSE),
- COALESCE((SELECT ilock FROM changes WHERE id = i), FALSE)
- );
+ SELECT INTO x id, ihid, ilock FROM changes c WHERE type = xtype AND itemid = xitemid AND rev = xrev;
+ INSERT INTO edit_revision (type, itemid, ihid, ilock) VALUES (xtype, xitemid, COALESCE(x.ihid, FALSE), COALESCE(x.ilock, FALSE));
+ RETURN x.id;
END;
$$ LANGUAGE plpgsql;
@@ -223,305 +221,145 @@ $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION edit_commit() RETURNS edit_rettype AS $$
DECLARE
- r edit_rettype;
- t dbentry_type;
- i integer;
+ ret edit_rettype;
+ xtype dbentry_type;
BEGIN
- SELECT type INTO t FROM edit_revision;
- SELECT iid INTO i FROM edit_revision;
+ SELECT type INTO xtype FROM edit_revision;
+ SELECT itemid INTO ret.itemid FROM edit_revision;
-- figure out revision number
- IF i IS NULL THEN
- r.rev := 1;
- ELSE
- SELECT c.rev+1 INTO r.rev FROM changes c
- JOIN ( SELECT id FROM vn_rev WHERE t = 'v' AND vid = i
- UNION SELECT id FROM releases_rev WHERE t = 'r' AND rid = i
- UNION SELECT id FROM producers_rev WHERE t = 'p' AND pid = i
- UNION SELECT id FROM chars_rev WHERE t = 'c' AND cid = i
- UNION SELECT id FROM staff_rev WHERE t = 's' AND sid = i
- ) x(id) ON x.id = c.id
- ORDER BY c.id DESC
- LIMIT 1;
- END IF;
- -- insert change
- INSERT INTO changes (type, requester, ip, comments, ihid, ilock, rev)
- SELECT t, requester, ip, comments, ihid, ilock, r.rev
- FROM edit_revision
- RETURNING id INTO r.cid;
+ SELECT MAX(rev)+1 INTO ret.rev FROM changes WHERE type = xtype AND itemid = ret.itemid;
+ SELECT COALESCE(ret.rev, 1) INTO ret.rev;
-- insert DB item
- IF i IS NULL THEN
- CASE t
- WHEN 'v' THEN INSERT INTO vn (latest) VALUES (0) RETURNING id INTO r.iid;
- WHEN 'r' THEN INSERT INTO releases (latest) VALUES (0) RETURNING id INTO r.iid;
- WHEN 'p' THEN INSERT INTO producers (latest) VALUES (0) RETURNING id INTO r.iid;
- WHEN 'c' THEN INSERT INTO chars (latest) VALUES (0) RETURNING id INTO r.iid;
- WHEN 's' THEN INSERT INTO staff (latest) VALUES (0) RETURNING id INTO r.iid;
+ IF ret.itemid IS NULL THEN
+ CASE xtype
+ WHEN 'v' THEN INSERT INTO vn DEFAULT VALUES RETURNING id INTO ret.itemid;
+ WHEN 'r' THEN INSERT INTO releases DEFAULT VALUES RETURNING id INTO ret.itemid;
+ WHEN 'p' THEN INSERT INTO producers DEFAULT VALUES RETURNING id INTO ret.itemid;
+ WHEN 'c' THEN INSERT INTO chars DEFAULT VALUES RETURNING id INTO ret.itemid;
+ WHEN 's' THEN INSERT INTO staff DEFAULT VALUES RETURNING id INTO ret.itemid;
END CASE;
- ELSE
- r.iid := i;
- END IF;
- RETURN r;
-END;
-$$ LANGUAGE plpgsql;
-
-
-
-CREATE OR REPLACE FUNCTION edit_vn_init(cid integer) RETURNS void AS $$
-#variable_conflict use_variable
-BEGIN
- -- create tables, based on existing tables (so that the column types are always synchronised)
- BEGIN
- CREATE TEMPORARY TABLE edit_vn (LIKE vn_rev INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn DROP COLUMN id;
- ALTER TABLE edit_vn DROP COLUMN vid;
- CREATE TEMPORARY TABLE edit_vn_anime (LIKE vn_anime INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn_anime DROP COLUMN vid;
- CREATE TEMPORARY TABLE edit_vn_relations (LIKE vn_relations INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn_relations DROP COLUMN vid1;
- ALTER TABLE edit_vn_relations RENAME COLUMN vid2 TO vid;
- CREATE TEMPORARY TABLE edit_vn_screenshots (LIKE vn_screenshots INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn_screenshots DROP COLUMN vid;
- CREATE TEMPORARY TABLE edit_vn_staff (LIKE vn_staff INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn_staff DROP COLUMN vid;
- CREATE TEMPORARY TABLE edit_vn_seiyuu (LIKE vn_seiyuu INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_vn_seiyuu DROP COLUMN vid;
- EXCEPTION WHEN duplicate_table THEN
- TRUNCATE edit_vn, edit_vn_anime, edit_vn_relations, edit_vn_screenshots, edit_vn_staff, edit_vn_seiyuu;
- END;
- PERFORM edit_revtable('v', cid);
- -- new VN, load defaults
- IF cid IS NULL THEN
- INSERT INTO edit_vn DEFAULT VALUES;
- -- otherwise, load revision
- ELSE
- INSERT INTO edit_vn SELECT title, alias, img_nsfw, length, "desc", l_wp, image, l_encubed, l_renai, original FROM vn_rev WHERE id = cid;
- INSERT INTO edit_vn_anime SELECT aid FROM vn_anime WHERE vid = cid;
- INSERT INTO edit_vn_relations SELECT vid2, relation, official FROM vn_relations WHERE vid1 = cid;
- INSERT INTO edit_vn_screenshots SELECT scr, nsfw, rid FROM vn_screenshots WHERE vid = cid;
- INSERT INTO edit_vn_staff SELECT aid, role, note FROM vn_staff WHERE vid = cid;
- INSERT INTO edit_vn_seiyuu SELECT aid, vs.cid, note FROM vn_seiyuu vs WHERE vid = cid;
- END IF;
-END;
-$$ LANGUAGE plpgsql;
-
-
-
-CREATE OR REPLACE FUNCTION edit_vn_commit() RETURNS edit_rettype AS $$
-DECLARE
- r edit_rettype;
-BEGIN
- IF (SELECT COUNT(*) FROM edit_vn) <> 1 THEN
- RAISE 'edit_vn must have exactly one row!';
- END IF;
- SELECT INTO r * FROM edit_commit();
- INSERT INTO vn_rev SELECT r.cid, r.iid, title, alias, img_nsfw, length, "desc", l_wp, image, l_encubed, l_renai, original FROM edit_vn;
- INSERT INTO vn_anime SELECT r.cid, aid FROM edit_vn_anime;
- INSERT INTO vn_relations SELECT r.cid, vid, relation, official FROM edit_vn_relations;
- INSERT INTO vn_screenshots SELECT r.cid, scr, nsfw, rid FROM edit_vn_screenshots;
- INSERT INTO vn_staff SELECT r.cid, aid, role, note FROM edit_vn_staff;
- INSERT INTO vn_seiyuu SELECT r.cid, aid, cid, note FROM edit_vn_seiyuu;
- UPDATE vn SET latest = r.cid WHERE id = r.iid;
- RETURN r;
-END;
-$$ LANGUAGE plpgsql;
-
-
-
-CREATE OR REPLACE FUNCTION edit_release_init(cid integer) RETURNS void AS $$
-BEGIN
- -- temp. tables
- BEGIN
- CREATE TEMPORARY TABLE edit_release (LIKE releases_rev INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release DROP COLUMN id;
- ALTER TABLE edit_release DROP COLUMN rid;
- CREATE TEMPORARY TABLE edit_release_lang (LIKE releases_lang INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release_lang DROP COLUMN rid;
- CREATE TEMPORARY TABLE edit_release_media (LIKE releases_media INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release_media DROP COLUMN rid;
- CREATE TEMPORARY TABLE edit_release_platforms (LIKE releases_platforms INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release_platforms DROP COLUMN rid;
- CREATE TEMPORARY TABLE edit_release_producers (LIKE releases_producers INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release_producers DROP COLUMN rid;
- CREATE TEMPORARY TABLE edit_release_vn (LIKE releases_vn INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_release_vn DROP COLUMN rid;
- EXCEPTION WHEN duplicate_table THEN
- TRUNCATE edit_release, edit_release_lang, edit_release_media, edit_release_platforms, edit_release_producers, edit_release_vn;
- END;
- PERFORM edit_revtable('r', cid);
- -- new release
- IF cid IS NULL THEN
- INSERT INTO edit_release DEFAULT VALUES;
- -- load revision
- ELSE
- INSERT INTO edit_release SELECT title, original, type, website, released, notes, minage, gtin, patch, catalog, resolution, voiced, freeware, doujin, ani_story, ani_ero FROM releases_rev WHERE id = cid;
- INSERT INTO edit_release_lang SELECT lang FROM releases_lang WHERE rid = cid;
- INSERT INTO edit_release_media SELECT medium, qty FROM releases_media WHERE rid = cid;
- INSERT INTO edit_release_platforms SELECT platform FROM releases_platforms WHERE rid = cid;
- INSERT INTO edit_release_producers SELECT pid, developer, publisher FROM releases_producers WHERE rid = cid;
- INSERT INTO edit_release_vn SELECT vid FROM releases_vn WHERE rid = cid;
END IF;
+ -- insert change
+ INSERT INTO changes (type, itemid, rev, requester, ip, comments, ihid, ilock)
+ SELECT type, ret.itemid, ret.rev, requester, ip, comments, ihid, ilock FROM edit_revision RETURNING id INTO ret.chid;
+ RETURN ret;
END;
$$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION edit_release_commit() RETURNS edit_rettype AS $$
+-- Check for stuff to be done when an item has been changed
+CREATE OR REPLACE FUNCTION edit_committed(xtype dbentry_type, xedit edit_rettype) RETURNS void AS $$
DECLARE
- r edit_rettype;
+ xoldchid integer;
BEGIN
- IF (SELECT COUNT(*) FROM edit_release) <> 1 THEN
- RAISE 'edit_release must have exactly one row!';
- ELSIF NOT EXISTS(SELECT 1 FROM edit_release_vn) THEN
- RAISE 'edit_release_vn must have at least one row!';
+ SELECT id INTO xoldchid FROM changes WHERE type = xtype AND itemid = xedit.itemid AND rev = xedit.rev-1;
+
+ -- Set producers.rgraph to NULL and notify when:
+ -- 1. There's a new producer entry with some relations
+ -- 2. The producer name/type/language has changed
+ -- 3. The producer relations have been changed
+ IF xtype = 'p' THEN
+ IF -- 1.
+ (xoldchid IS NULL AND EXISTS(SELECT 1 FROM producers_relations_hist WHERE chid = xedit.chid))
+ OR (xoldchid IS NOT NULL AND (
+ -- 2.
+ EXISTS(SELECT 1 FROM producers_hist p1, producers_hist p2 WHERE (p2.name <> p1.name OR p2.type <> p1.type OR p2.lang <> p1.lang) AND p1.chid = xoldchid AND p2.chid = xedit.chid)
+ -- 3.
+ OR EXISTS(SELECT pid, relation FROM producers_relations_hist WHERE chid = xoldchid EXCEPT SELECT pid, relation FROM producers_relations_hist WHERE chid = xedit.chid)
+ OR EXISTS(SELECT pid, relation FROM producers_relations_hist WHERE chid = xedit.chid EXCEPT SELECT pid, relation FROM producers_relations_hist WHERE chid = xoldchid)
+ ))
+ THEN
+ UPDATE producers SET rgraph = NULL WHERE id = xedit.itemid;
+ NOTIFY relgraph; -- This notify is not done by the producer_relgraph_notify trigger for new entries or if rgraph was already NULL
+ END IF;
END IF;
- SELECT INTO r * FROM edit_commit();
- INSERT INTO releases_rev SELECT r.cid, r.iid, title, original, type, website, released, notes, minage, gtin, patch, catalog, resolution, voiced, freeware, doujin, ani_story, ani_ero FROM edit_release;
- INSERT INTO releases_lang SELECT r.cid, lang FROM edit_release_lang;
- INSERT INTO releases_media SELECT r.cid, medium, qty FROM edit_release_media;
- INSERT INTO releases_platforms SELECT r.cid, platform FROM edit_release_platforms;
- INSERT INTO releases_producers SELECT pid, r.cid, developer, publisher FROM edit_release_producers;
- INSERT INTO releases_vn SELECT r.cid, vid FROM edit_release_vn;
- UPDATE releases SET latest = r.cid WHERE id = r.iid;
- RETURN r;
-END;
-$$ LANGUAGE plpgsql;
-
-
-CREATE OR REPLACE FUNCTION edit_producer_init(cid integer) RETURNS void AS $$
-BEGIN
- BEGIN
- CREATE TEMPORARY TABLE edit_producer (LIKE producers_rev INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_producer DROP COLUMN id;
- ALTER TABLE edit_producer DROP COLUMN pid;
- CREATE TEMPORARY TABLE edit_producer_relations (LIKE producers_relations INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_producer_relations DROP COLUMN pid1;
- ALTER TABLE edit_producer_relations RENAME COLUMN pid2 TO pid;
- EXCEPTION WHEN duplicate_table THEN
- TRUNCATE edit_producer, edit_producer_relations;
- END;
- PERFORM edit_revtable('p', cid);
- -- new producer
- IF cid IS NULL THEN
- INSERT INTO edit_producer DEFAULT VALUES;
- -- load revision
- ELSE
- INSERT INTO edit_producer SELECT type, name, original, website, lang, "desc", alias, l_wp FROM producers_rev WHERE id = cid;
- INSERT INTO edit_producer_relations SELECT pid2, relation FROM producers_relations WHERE pid1 = cid;
+ -- Set vn.rgraph to NULL and notify when:
+ -- 1. There's a new vn entry with some relations
+ -- 2. The vn title has changed
+ -- 3. The vn relations have been changed
+ IF xtype = 'v' THEN
+ IF -- 1.
+ (xoldchid IS NULL AND EXISTS(SELECT 1 FROM vn_relations_hist WHERE chid = xedit.chid))
+ OR (xoldchid IS NOT NULL AND (
+ -- 2.
+ EXISTS(SELECT 1 FROM vn_hist v1, vn_hist v2 WHERE v2.title <> v1.title AND v1.chid = xoldchid AND v2.chid = xedit.chid)
+ -- 3.
+ OR EXISTS(SELECT vid, relation, official FROM vn_relations_hist WHERE chid = xoldchid EXCEPT SELECT vid, relation, official FROM vn_relations_hist WHERE chid = xedit.chid)
+ OR EXISTS(SELECT vid, relation, official FROM vn_relations_hist WHERE chid = xedit.chid EXCEPT SELECT vid, relation, official FROM vn_relations_hist WHERE chid = xoldchid)
+ ))
+ THEN
+ UPDATE vn SET rgraph = NULL WHERE id = xedit.itemid;
+ NOTIFY relgraph;
+ END IF;
END IF;
-END;
-$$ LANGUAGE plpgsql;
-
-
-CREATE OR REPLACE FUNCTION edit_producer_commit() RETURNS edit_rettype AS $$
-DECLARE
- r edit_rettype;
-BEGIN
- IF (SELECT COUNT(*) FROM edit_producer) <> 1 THEN
- RAISE 'edit_producer must have exactly one row!';
+ -- Set c_search to NULL and notify when
+ -- 1. A new VN entry is created
+ -- 2. The vn title/original/alias has changed
+ IF xtype = 'v' THEN
+ IF -- 1.
+ xoldchid IS NULL OR
+ -- 2.
+ EXISTS(SELECT 1 FROM vn_hist v1, vn_hist v2 WHERE (v2.title <> v1.title OR v2.original <> v1.original OR v2.alias <> v1.alias) AND v1.chid = xoldchid AND v2.chid = xedit.chid)
+ THEN
+ UPDATE vn SET c_search = NULL WHERE id = xedit.itemid;
+ NOTIFY vnsearch;
+ END IF;
END IF;
- SELECT INTO r * FROM edit_commit();
- INSERT INTO producers_rev SELECT r.cid, r.iid, type, name, original, website, lang, "desc", alias, l_wp FROM edit_producer;
- INSERT INTO producers_relations SELECT r.cid, pid, relation FROM edit_producer_relations;
- UPDATE producers SET latest = r.cid WHERE id = r.iid;
- RETURN r;
-END;
-$$ LANGUAGE plpgsql;
-
-
-CREATE OR REPLACE FUNCTION edit_char_init(hid integer) RETURNS void AS $$
-BEGIN
- BEGIN
- CREATE TEMPORARY TABLE edit_char (LIKE chars_rev INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_char DROP COLUMN id;
- ALTER TABLE edit_char DROP COLUMN cid;
- CREATE TEMPORARY TABLE edit_char_traits (LIKE chars_traits INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_char_traits DROP COLUMN cid;
- CREATE TEMPORARY TABLE edit_char_vns (LIKE chars_vns INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_char_vns DROP COLUMN cid;
- EXCEPTION WHEN duplicate_table THEN
- TRUNCATE edit_char, edit_char_traits, edit_char_vns;
- END;
- PERFORM edit_revtable('c', hid);
- -- new char
- IF hid IS NULL THEN
- INSERT INTO edit_char DEFAULT VALUES;
- -- load revision
- ELSE
- INSERT INTO edit_char SELECT name, original, alias, image, "desc", gender, s_bust, s_waist, s_hip, b_month, b_day, height, weight, bloodt, main, main_spoil FROM chars_rev WHERE id = hid;
- INSERT INTO edit_char_traits SELECT tid, spoil FROM chars_traits WHERE cid = hid;
- INSERT INTO edit_char_vns SELECT vid, rid, spoil, role FROM chars_vns WHERE cid = hid;
+ -- Set related vn.c_search columns to NULL and notify when
+ -- 1. A new release is created
+ -- 2. A release has been hidden or unhidden
+ -- 3. The release title/original has changed
+ -- 4. The releases_vn table differs from a previous revision
+ IF xtype = 'r' THEN
+ IF -- 1.
+ xoldchid IS NULL OR
+ -- 2.
+ EXISTS(SELECT 1 FROM changes c1, changes c2 WHERE c1.ihid IS DISTINCT FROM c2.ihid AND c1.id = xedit.chid AND c2.id = xoldchid) OR
+ -- 3.
+ EXISTS(SELECT 1 FROM releases_hist r1, releases_hist r2 WHERE (r2.title <> r1.title OR r2.original <> r1.original) AND r1.chid = xoldchid AND r2.chid = xedit.chid) OR
+ -- 4.
+ EXISTS(SELECT vid FROM releases_vn_hist WHERE chid = xoldchid EXCEPT SELECT vid FROM releases_vn_hist WHERE chid = xedit.chid) OR
+ EXISTS(SELECT vid FROM releases_vn_hist WHERE chid = xedit.chid EXCEPT SELECT vid FROM releases_vn_hist WHERE chid = xoldchid)
+ THEN
+ UPDATE vn SET c_search = NULL WHERE id IN(SELECT vid FROM releases_vn_hist WHERE chid IN(xedit.chid, xoldchid));
+ NOTIFY vnsearch;
+ END IF;
END IF;
-END;
-$$ LANGUAGE plpgsql;
-
-
-CREATE OR REPLACE FUNCTION edit_char_commit() RETURNS edit_rettype AS $$
-DECLARE
- r edit_rettype;
-BEGIN
- IF (SELECT COUNT(*) FROM edit_char) <> 1 THEN
- RAISE 'edit_char must have exactly one row!';
+ -- Call update_vncache() for related VNs when a release has been created or edited
+ -- (This could be made more specific, but update_vncache() is fast enough that it's not worth the complexity)
+ IF xtype = 'r' THEN
+ PERFORM update_vncache(vid) FROM (
+ SELECT DISTINCT vid FROM releases_vn_hist WHERE chid IN(xedit.chid, xoldchid)
+ ) AS v(vid);
END IF;
- SELECT INTO r * FROM edit_commit();
- INSERT INTO chars_rev SELECT r.cid, r.iid, name, original, alias, image, "desc", gender, s_bust, s_waist, s_hip, b_month, b_day, height, weight, bloodt, main, main_spoil FROM edit_char;
- INSERT INTO chars_traits SELECT r.cid, tid, spoil FROM edit_char_traits;
- INSERT INTO chars_vns SELECT r.cid, vid, rid, spoil, role FROM edit_char_vns;
- UPDATE chars SET latest = r.cid WHERE id = r.iid;
- RETURN r;
-END;
-$$ LANGUAGE plpgsql;
-
-
-CREATE OR REPLACE FUNCTION edit_staff_init(cid integer) RETURNS void AS $$
-BEGIN
- BEGIN
- CREATE TEMPORARY TABLE edit_staff (LIKE staff_rev INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_staff DROP COLUMN id;
- ALTER TABLE edit_staff DROP COLUMN sid;
- CREATE TEMPORARY TABLE edit_staff_aliases (LIKE staff_alias INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
- ALTER TABLE edit_staff_aliases DROP COLUMN rid;
- EXCEPTION WHEN duplicate_table THEN
- TRUNCATE edit_staff, edit_staff_aliases;
- END;
- PERFORM edit_revtable('s', cid);
- -- new staff member
- IF cid IS NULL THEN
- INSERT INTO edit_staff (aid) VALUES (0);
- -- load revision
- ELSE
- INSERT INTO edit_staff SELECT aid, gender, lang, "desc", l_wp, l_site, l_twitter, l_anidb FROM staff_rev WHERE id = cid;
- INSERT INTO edit_staff_aliases SELECT id, name, original FROM staff_alias WHERE rid = cid;
+ -- Call notify_dbdel() if an entry has been deleted
+ -- Call notify_listdel() if a vn/release entry has been deleted
+ IF xoldchid IS NOT NULL
+ AND EXISTS(SELECT 1 FROM changes WHERE id = xoldchid AND NOT ihid)
+ AND EXISTS(SELECT 1 FROM changes WHERE id = xedit.chid AND ihid)
+ THEN
+ PERFORM notify_dbdel(xtype, xedit);
+ IF xtype = 'v' OR xtype = 'r' THEN
+ PERFORM notify_listdel(xtype, xedit);
+ END IF;
END IF;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION edit_staff_commit() RETURNS edit_rettype AS $$
-DECLARE
- r edit_rettype;
-BEGIN
- IF (SELECT COUNT(*) FROM edit_staff) <> 1 THEN
- RAISE 'edit_staff must have exactly one row!';
+ -- Call notify_dbedit() if a non-hidden entry has been edited
+ IF xoldchid IS NOT NULL AND EXISTS(SELECT 1 FROM changes WHERE id = xedit.chid AND NOT ihid)
+ THEN
+ PERFORM notify_dbedit(xtype, xedit);
END IF;
- SELECT INTO r * FROM edit_commit();
- INSERT INTO staff_alias (id, rid, name, original)
- SELECT id, r.cid, name, original FROM edit_staff_aliases;
- INSERT INTO staff_rev (id, sid, aid, gender, lang, "desc", l_wp, l_site, l_twitter, l_anidb)
- SELECT r.cid, r.iid, aid, gender, lang, "desc", l_wp, l_site, l_twitter, l_anidb FROM edit_staff;
- UPDATE staff SET latest = r.cid WHERE id = r.iid;
- RETURN r;
END;
$$ LANGUAGE plpgsql;
-
-
----------------------------------------------------------
-- trigger functions --
----------------------------------------------------------
@@ -627,18 +465,17 @@ CREATE OR REPLACE FUNCTION update_vnlist_rlist() RETURNS trigger AS $$
BEGIN
-- 1.
IF TG_TABLE_NAME = 'vnlists' THEN
- DELETE FROM rlists WHERE uid = OLD.uid AND rid IN(SELECT r.id
+ DELETE FROM rlists WHERE uid = OLD.uid AND rid IN(SELECT rv.id
-- fetch all related rows in rlists
FROM releases_vn rv
- JOIN releases r ON r.latest = rv.rid
- JOIN rlists rl ON rl.rid = r.id
+ JOIN rlists rl ON rl.rid = rv.id
WHERE rv.vid = OLD.vid AND rl.uid = OLD.uid
-- and test for a corresponding row in vnlists
AND NOT EXISTS(
SELECT 1
FROM releases_vn rvi
JOIN vnlists vl ON vl.vid = rvi.vid AND uid = OLD.uid
- WHERE rvi.rid = r.latest
+ WHERE rvi.id = rv.id
));
-- 2.
@@ -646,15 +483,13 @@ BEGIN
INSERT INTO vnlists (uid, vid) SELECT NEW.uid, rv.vid
-- all VNs linked to the release
FROM releases_vn rv
- JOIN releases r ON rv.rid = r.latest
- WHERE r.id = NEW.rid
+ WHERE rv.id = NEW.rid
-- but only if there are no corresponding rows in vnlists yet
AND NOT EXISTS(
SELECT 1
FROM releases_vn rvi
- JOIN releases ri ON rvi.rid = ri.latest
JOIN vnlists vl ON vl.vid = rvi.vid
- WHERE ri.id = NEW.rid AND vl.uid = NEW.uid
+ WHERE rvi.id = NEW.rid AND vl.uid = NEW.uid
);
END IF;
RETURN NULL;
@@ -670,33 +505,16 @@ $$ LANGUAGE plpgsql;
--- Update vn.rgraph column and send notify when a relation graph needs to be regenerated
--- 1. NOTIFY is sent on VN edit or insert or change in vn.rgraph, when rgraph = NULL and entries in vn_relations
--- vn.rgraph is set to NULL when:
--- 2. UPDATE on vn where c_released or c_languages has changed
--- 3. VN edit of which the title differs from previous revision
--- 4. VN edit with items in vn_relations that differ from previous
+-- 1. Send a notify when vn.rgraph is set to NULL, and there are related entries in vn_relations
+-- 2. Set rgraph to NULL when c_languages or c_released has changed
CREATE OR REPLACE FUNCTION vn_relgraph_notify() RETURNS trigger AS $$
BEGIN
- -- 1.
- IF NEW.rgraph IS DISTINCT FROM OLD.rgraph OR NEW.latest IS DISTINCT FROM OLD.latest THEN
- IF NEW.rgraph IS NULL AND EXISTS(SELECT 1 FROM vn_relations WHERE vid1 = NEW.latest) THEN
+ IF EXISTS(SELECT 1 FROM vn_relations WHERE id = NEW.id) THEN
+ -- 1.
+ IF NEW.rgraph IS NULL THEN
NOTIFY relgraph;
- END IF;
- END IF;
- IF NEW.rgraph IS NOT NULL THEN
- IF
- -- 2.
- OLD.c_released IS DISTINCT FROM NEW.c_released
- OR OLD.c_languages IS DISTINCT FROM NEW.c_languages
- OR OLD.latest <> 0 AND OLD.latest IS DISTINCT FROM NEW.latest AND (
- -- 3.
- EXISTS(SELECT 1 FROM vn_rev v1, vn_rev v2 WHERE v2.title <> v1.title AND v1.id = OLD.latest AND v2.id = NEW.latest)
- -- 4. (not-really-readable method of comparing two query results)
- OR EXISTS(SELECT vid2, relation, official FROM vn_relations WHERE vid1 = OLD.latest EXCEPT SELECT vid2, relation, official FROM vn_relations WHERE vid1 = NEW.latest)
- OR (SELECT COUNT(*) FROM vn_relations WHERE vid1 = OLD.latest) <> (SELECT COUNT(*) FROM vn_relations WHERE vid1 = NEW.latest)
- )
- THEN
+ -- 2.
+ ELSE
UPDATE vn SET rgraph = NULL WHERE id = NEW.id;
END IF;
END IF;
@@ -705,30 +523,11 @@ END;
$$ LANGUAGE plpgsql;
-
--- Same as above for producers, with slight differences in the steps:
--- There is no 2, and
--- 3 = Producer edit of which the name, language or type differs from the previous revision
+-- Send a notify when producers.rgraph is set to NULL, and there are related entries in producers_relations
CREATE OR REPLACE FUNCTION producer_relgraph_notify() RETURNS trigger AS $$
BEGIN
- -- 1.
- IF NEW.rgraph IS DISTINCT FROM OLD.rgraph OR NEW.latest IS DISTINCT FROM OLD.latest THEN
- IF NEW.rgraph IS NULL AND EXISTS(SELECT 1 FROM producers_relations WHERE pid1 = NEW.latest) THEN
- NOTIFY relgraph;
- END IF;
- END IF;
- IF NEW.rgraph IS NOT NULL THEN
- -- 2.
- IF OLD.latest <> 0 AND OLD.latest IS DISTINCT FROM NEW.latest AND (
- -- 3.
- EXISTS(SELECT 1 FROM producers_rev p1, producers_rev p2 WHERE (p2.name <> p1.name OR p2.type <> p1.type OR p2.lang <> p1.lang) AND p1.id = OLD.latest AND p2.id = NEW.latest)
- -- 4. (not-really-readable method of comparing two query results)
- OR EXISTS(SELECT p1.pid2, p1.relation FROM producers_relations p1 WHERE p1.pid1 = OLD.latest EXCEPT SELECT p2.pid2, p2.relation FROM producers_relations p2 WHERE p2.pid1 = NEW.latest)
- OR (SELECT COUNT(*) FROM producers_relations WHERE pid1 = OLD.latest) <> (SELECT COUNT(*) FROM producers_relations WHERE pid1 = NEW.latest)
- )
- THEN
- UPDATE producers SET rgraph = NULL WHERE id = NEW.id;
- END IF;
+ IF EXISTS(SELECT 1 FROM producers_relations WHERE id = NEW.id) THEN
+ NOTIFY relgraph;
END IF;
RETURN NULL;
END;
@@ -754,80 +553,16 @@ $$ LANGUAGE plpgsql;
--- call update_vncache() when a release is added, edited, hidden or unhidden
-CREATE OR REPLACE FUNCTION release_vncache_update() RETURNS trigger AS $$
-BEGIN
- PERFORM update_vncache(vid) FROM (
- SELECT DISTINCT vid FROM releases_vn WHERE rid = OLD.latest OR rid = NEW.latest
- ) AS v(vid);
- RETURN NULL;
-END;
-$$ LANGUAGE plpgsql;
-
-
-
--- update (vn|release|producer|char).(hidden|locked) on a new revision
--- NOTE: this is a /before/ trigger, it modifies NEW
-CREATE OR REPLACE FUNCTION update_hidlock() RETURNS trigger AS $$
-DECLARE
- r record;
-BEGIN
- SELECT INTO r ihid, ilock FROM changes WHERE id = NEW.latest;
- NEW.hidden := r.ihid;
- NEW.locked := r.ilock;
- RETURN NEW;
-END;
-$$ LANGUAGE plpgsql;
-
-
-
--- Check for updates to vn.c_search
--- 1. NOTIFY is sent when vn.c_search goes from non-NULL to NULL
--- vn.c_search is set to NULL when:
--- 2. VN add/edit of which the title/original/alias fields differ from previous revision
--- 3. Release gets hidden or unhidden
--- 4. Release add/edit of which the title/original/vn fields differ from the previous revision
+-- Send a vnsearch notification when the c_search column is set to NULL.
CREATE OR REPLACE FUNCTION vn_vnsearch_notify() RETURNS trigger AS $$
-BEGIN
- IF TG_TABLE_NAME = 'vn' THEN
- -- 1.
- IF NEW.c_search IS NULL THEN
- NOTIFY vnsearch;
- -- 2.
- ELSIF NEW.latest IS DISTINCT FROM OLD.latest THEN
- IF EXISTS(SELECT 1 FROM vn_rev v1, vn_rev v2
- WHERE v1.id = OLD.latest AND v2.id = NEW.latest
- AND (v1.title IS DISTINCT FROM v2.title OR v1.original IS DISTINCT FROM v2.original OR v1.alias IS DISTINCT FROM v2.alias)
- ) THEN
- UPDATE vn SET c_search = NULL WHERE id = NEW.id;
- END IF;
- END IF;
- ELSIF TG_TABLE_NAME = 'releases' THEN
- -- 3. & 4.
- IF NEW.hidden IS DISTINCT FROM OLD.hidden OR (
- NEW.latest IS DISTINCT FROM OLD.latest AND (
- EXISTS(
- SELECT 1 FROM releases_rev r1, releases_rev r2
- WHERE r1.id = OLD.latest AND r2.id = NEW.latest
- AND (r1.title IS DISTINCT FROM r2.title OR r1.original IS DISTINCT FROM r2.original)
- )
- OR EXISTS(SELECT vid FROM releases_vn WHERE rid = OLD.latest EXCEPT SELECT vid FROM releases_vn WHERE rid = NEW.latest)
- OR (SELECT COUNT(*) FROM releases_vn WHERE rid = OLD.latest) <> (SELECT COUNT(*) FROM releases_vn WHERE rid = NEW.latest)
- )) THEN
- UPDATE vn SET c_search = NULL WHERE id IN(SELECT vid FROM releases_vn WHERE rid = OLD.latest OR rid = NEW.latest);
- END IF;
- END IF;
- RETURN NULL;
-END;
+ BEGIN NOTIFY vnsearch; RETURN NULL; END;
$$ LANGUAGE plpgsql;
-
----------------------------------------------------------
-- notification functions --
--- (these are, in fact, also triggers) --
----------------------------------------------------------
@@ -854,107 +589,70 @@ END;
$$ LANGUAGE plpgsql;
--- called on UPDATE vn / producers / releases / chars when (NOT OLD.hidden AND NEW.hidden)
-CREATE OR REPLACE FUNCTION notify_dbdel() RETURNS trigger AS $$
-BEGIN
+
+-- called when an entry has been deleted
+CREATE OR REPLACE FUNCTION notify_dbdel(xtype dbentry_type, xedit edit_rettype) RETURNS void AS $$
INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
- SELECT DISTINCT 'dbdel'::notification_ntype,
- (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' WHEN 'producers' THEN 'p' WHEN 'staff' THEN 's' ELSE 'c' END)::notification_ltype,
- h.requester, NEW.id, h2.rev, x.title, h2.requester
- -- look for changes of the deleted entry
- -- this method may look a bit unintuitive, but it's way faster than doing LEFT JOINs
+ SELECT DISTINCT 'dbdel'::notification_ntype, xtype::text::notification_ltype, h.requester, xedit.itemid, xedit.rev, x.title, h2.requester
FROM changes h
- JOIN ( SELECT vr.id, vr2.title FROM vn_rev vr
- JOIN vn v ON v.id = vr.vid JOIN vn_rev vr2 ON vr2.id = v.latest
- WHERE TG_TABLE_NAME = 'vn' AND vr.vid = NEW.id
- UNION SELECT rr.id, rr2.title FROM releases_rev rr
- JOIN releases r ON r.id = rr.rid JOIN releases_rev rr2 ON rr2.id = r.latest
- WHERE TG_TABLE_NAME = 'releases' AND rr.rid = NEW.id
- UNION SELECT pr.id, pr2.name FROM producers_rev pr
- JOIN producers p ON p.id = pr.pid JOIN producers_rev pr2 ON pr2.id = p.latest
- WHERE TG_TABLE_NAME = 'producers' AND pr.pid = NEW.id
- UNION SELECT cr.id, cr2.name FROM chars_rev cr
- JOIN chars c ON c.id = cr.cid JOIN chars_rev cr2 ON cr2.id = c.latest
- WHERE TG_TABLE_NAME = 'chars' AND cr.cid = NEW.id
- UNION SELECT sr.id, sa.name FROM staff_rev sr
- JOIN staff s ON s.id = sr.sid JOIN staff_rev sr2 ON sr2.id = s.latest JOIN staff_alias sa ON sa.id = sr2.aid AND sa.rid = sr2.id
- WHERE TG_TABLE_NAME = 'staff' AND sr.sid = NEW.id
- ) x(id, title) ON h.id = x.id
-- join info about the deletion itself
- JOIN changes h2 ON h2.id = NEW.latest
- WHERE h.requester <> 1 -- exclude Multi
- -- exclude the user who deleted the entry
- AND h.requester <> h2.requester;
- RETURN NULL;
-END;
-$$ LANGUAGE plpgsql;
+ JOIN changes h2 ON h2.id = xedit.chid
+ -- Fetch the latest name/title of the entry
+ -- this method may look a bit unintuitive, but it's way faster than doing LEFT JOINs
+ JOIN ( SELECT v.title FROM vn v WHERE xtype = 'v' AND v.id = xedit.itemid
+ UNION SELECT r.title FROM releases r WHERE xtype = 'r' AND r.id = xedit.itemid
+ UNION SELECT p.name FROM producers p WHERE xtype = 'p' AND p.id = xedit.itemid
+ UNION SELECT c.name FROM chars c WHERE xtype = 'c' AND c.id = xedit.itemid
+ UNION SELECT sa.name FROM staff s JOIN staff_alias sa ON sa.aid = s.aid WHERE xtype = 's' AND s.id = xedit.itemid
+ ) x(title) ON true
+ WHERE h.type = xtype AND h.itemid = xedit.itemid
+ AND h.requester <> 1 -- exclude Multi
+ AND h.requester <> h2.requester; -- exclude the user who deleted the entry
+$$ LANGUAGE sql;
--- called on UPDATE vn / releases when (NOT OLD.hidden AND NEW.hidden)
-CREATE OR REPLACE FUNCTION notify_listdel() RETURNS trigger AS $$
-BEGIN
+
+-- Called when a non-deleted item has been edited.
+CREATE OR REPLACE FUNCTION notify_dbedit(xtype dbentry_type, xedit edit_rettype) RETURNS void AS $$
+ INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
+ SELECT DISTINCT 'dbedit'::notification_ntype, xtype::text::notification_ltype, h.requester, xedit.itemid, xedit.rev, x.title, h2.requester
+ FROM changes h
+ -- join info about the edit itself
+ JOIN changes h2 ON h2.id = xedit.chid
+ -- Fetch the latest name/title of the entry
+ JOIN ( SELECT v.title FROM vn v WHERE xtype = 'v' AND v.id = xedit.itemid
+ UNION SELECT r.title FROM releases r WHERE xtype = 'r' AND r.id = xedit.itemid
+ UNION SELECT p.name FROM producers p WHERE xtype = 'p' AND p.id = xedit.itemid
+ UNION SELECT c.name FROM chars c WHERE xtype = 'c' AND c.id = xedit.itemid
+ UNION SELECT sa.name FROM staff s JOIN staff_alias sa ON sa.aid = s.aid WHERE xtype = 's' AND s.id = xedit.itemid
+ ) x(title) ON true
+ WHERE h.type = xtype AND h.itemid = xedit.itemid
+ AND h.requester <> h2.requester -- exclude the user who edited the entry
+ -- exclude users who don't want this notify
+ AND NOT EXISTS(SELECT 1 FROM users_prefs up WHERE uid = h.requester AND key = 'notify_nodbedit');
+$$ LANGUAGE sql;
+
+
+
+-- called when a VN/release entry has been deleted
+CREATE OR REPLACE FUNCTION notify_listdel(xtype dbentry_type, xedit edit_rettype) RETURNS void AS $$
INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
- SELECT DISTINCT 'listdel'::notification_ntype,
- (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' ELSE 'r' END)::notification_ltype,
- u.uid, NEW.id, c.rev, x.title, c.requester
+ SELECT DISTINCT 'listdel'::notification_ntype, xtype::text::notification_ltype, u.uid, xedit.itemid, xedit.rev, x.title, c.requester
-- look for users who should get this notify
FROM (
- -- voted on the VN
- SELECT uid FROM votes WHERE TG_TABLE_NAME = 'vn' AND vid = NEW.id
- -- VN in vnlist
- UNION SELECT uid FROM vnlists WHERE TG_TABLE_NAME = 'vn' AND vid = NEW.id
- -- VN in wishlist
- UNION SELECT uid FROM wlists WHERE TG_TABLE_NAME = 'vn' AND vid = NEW.id
- -- release in release list
- UNION SELECT uid FROM rlists WHERE TG_TABLE_NAME = 'releases' AND rid = NEW.id
+ SELECT uid FROM votes WHERE xtype = 'v' AND vid = xedit.itemid
+ UNION SELECT uid FROM vnlists WHERE xtype = 'v' AND vid = xedit.itemid
+ UNION SELECT uid FROM wlists WHERE xtype = 'v' AND vid = xedit.itemid
+ UNION SELECT uid FROM rlists WHERE xtype = 'r' AND rid = xedit.itemid
) u
-- fetch info about this edit
- JOIN changes c ON c.id = NEW.latest
+ JOIN changes c ON c.id = xedit.chid
JOIN (
- SELECT id, title FROM vn_rev WHERE TG_TABLE_NAME = 'vn' AND vid = NEW.id
- UNION SELECT id, title FROM releases_rev WHERE TG_TABLE_NAME = 'releases' AND rid = NEW.id
- ) x ON c.id = x.id
+ SELECT title FROM vn WHERE xtype = 'v' AND id = xedit.itemid
+ UNION SELECT title FROM releases WHERE xtype = 'r' AND id = xedit.itemid
+ ) x ON true
WHERE c.requester <> u.uid;
- RETURN NULL;
-END;
-$$ LANGUAGE plpgsql;
-
-
--- called on UPDATE vn / producers / releases / chars when (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden)
--- this trigger is very similar to notify_dbdel()
-CREATE OR REPLACE FUNCTION notify_dbedit() RETURNS trigger AS $$
-BEGIN
- INSERT INTO notifications (ntype, ltype, uid, iid, subid, c_title, c_byuser)
- SELECT DISTINCT 'dbedit'::notification_ntype,
- (CASE TG_TABLE_NAME WHEN 'vn' THEN 'v' WHEN 'releases' THEN 'r' WHEN 'producers' THEN 'p' WHEN 'staff' THEN 's' ELSE 'c' END)::notification_ltype,
- h.requester, NEW.id, h2.rev, x.title, h2.requester
- -- look for changes of the edited entry
- FROM changes h
- JOIN ( SELECT vr.id, vr2.title FROM vn_rev vr
- JOIN vn v ON v.id = vr.vid JOIN vn_rev vr2 ON vr2.id = v.latest
- WHERE TG_TABLE_NAME = 'vn' AND vr.vid = NEW.id
- UNION SELECT rr.id, rr2.title FROM releases_rev rr
- JOIN releases r ON r.id = rr.rid JOIN releases_rev rr2 ON rr2.id = r.latest
- WHERE TG_TABLE_NAME = 'releases' AND rr.rid = NEW.id
- UNION SELECT pr.id, pr2.name FROM producers_rev pr
- JOIN producers p ON p.id = pr.pid JOIN producers_rev pr2 ON pr2.id = p.latest
- WHERE TG_TABLE_NAME = 'producers' AND pr.pid = NEW.id
- UNION SELECT cr.id, cr2.name FROM chars_rev cr
- JOIN chars c ON c.id = cr.cid JOIN chars_rev cr2 ON cr2.id = c.latest
- WHERE TG_TABLE_NAME = 'chars' AND cr.cid = NEW.id
- UNION SELECT sr.id, sa.name FROM staff_rev sr
- JOIN staff s ON s.id = sr.sid JOIN staff_rev sr2 ON sr2.id = s.latest JOIN staff_alias sa ON sa.id = sr2.aid AND sa.rid = sr2.id
- WHERE TG_TABLE_NAME = 'staff' AND sr.sid = NEW.id
- ) x(id, title) ON h.id = x.id
- -- join info about the deletion itself
- JOIN changes h2 ON h2.id = NEW.latest
- -- exclude the user who edited the entry
- WHERE h.requester <> h2.requester
- -- exclude users who don't want this notify
- AND NOT EXISTS(SELECT 1 FROM users_prefs up WHERE uid = h.requester AND key = 'notify_nodbedit');
- RETURN NULL;
-END;
-$$ LANGUAGE plpgsql;
+$$ LANGUAGE sql;
-- called on INSERT INTO threads_posts when (NEW.num = 1)
diff --git a/util/sql/schema.sql b/util/sql/schema.sql
index 8dd960be..d2d11b64 100644
--- a/util/sql/schema.sql
+++ b/util/sql/schema.sql
@@ -1,3 +1,44 @@
+-- Convention for database items with version control:
+--
+-- CREATE TABLE items ( -- dbentry_type=x
+-- id SERIAL PRIMARY KEY,
+-- locked boolean NOT NULL DEFAULT FALSE,
+-- hidden boolean NOT NULL DEFAULT FALSE,
+-- -- item-specific columns here
+-- );
+-- CREATE TABLE items_hist ( -- History of the 'items' table
+-- chid integer NOT NULL, -- references changes.id
+-- -- item-specific columns here
+-- );
+--
+-- The '-- dbentry_type=x' comment is required, and is used by
+-- util/sqleditfunc.pl to generate the correct editing functions. The history
+-- of the 'locked' and 'hidden' flags is recorded in the changes table. It's
+-- possible for 'items' to have more item-specific columns than 'items_hist'.
+-- Some columns are caches or otherwise autogenerated, and do not need to be
+-- versioned.
+--
+-- item-related tables work roughly the same:
+--
+-- CREATE TABLE items_field (
+-- id integer, -- references items.id
+-- -- field-specific columns here
+-- );
+-- CREATE TABLE items_field_hist ( -- History of the 'items_field' table
+-- chid integer, -- references changes.id
+-- -- field-specific columns here
+-- );
+--
+-- The changes and *_hist tables contain all the data. In a sense, the other
+-- tables related to the item are just a cache/view into the latest versions.
+-- All modifications to the item tables has to go through the edit_* functions
+-- in func.sql, these are also responsible for keeping things synchronized.
+--
+-- Note: Every CREATE TABLE clause and each column should be on a separate
+-- line. This file is parsed by util/sqleditfunc.pl, and it doesn't implement a
+-- full SQL query parser.
+
+
-- affiliate_links
CREATE TABLE affiliate_links (
id SERIAL PRIMARY KEY,
@@ -26,29 +67,44 @@ CREATE TABLE anime (
-- changes
CREATE TABLE changes (
- id SERIAL NOT NULL PRIMARY KEY,
- type dbentry_type NOT NULL,
- rev integer NOT NULL DEFAULT 1,
- added timestamptz NOT NULL DEFAULT NOW(),
- requester integer NOT NULL DEFAULT 0,
- ip inet NOT NULL DEFAULT '0.0.0.0',
- comments text NOT NULL DEFAULT '',
- ihid boolean NOT NULL DEFAULT FALSE,
- ilock boolean NOT NULL DEFAULT FALSE
+ id SERIAL PRIMARY KEY,
+ type dbentry_type NOT NULL,
+ itemid integer NOT NULL,
+ rev integer NOT NULL DEFAULT 1,
+ added timestamptz NOT NULL DEFAULT NOW(),
+ requester integer NOT NULL DEFAULT 0,
+ ip inet NOT NULL DEFAULT '0.0.0.0',
+ comments text NOT NULL DEFAULT '',
+ ihid boolean NOT NULL DEFAULT FALSE,
+ ilock boolean NOT NULL DEFAULT FALSE
);
-- chars
-CREATE TABLE chars (
- id SERIAL PRIMARY KEY,
- latest integer NOT NULL DEFAULT 0,
- locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE
+CREATE TABLE chars ( -- dbentry_type=c
+ id SERIAL PRIMARY KEY,
+ locked boolean NOT NULL DEFAULT FALSE,
+ hidden boolean NOT NULL DEFAULT FALSE,
+ name varchar(250) NOT NULL DEFAULT '',
+ original varchar(250) NOT NULL DEFAULT '',
+ alias varchar(500) NOT NULL DEFAULT '',
+ image integer NOT NULL DEFAULT 0,
+ "desc" text NOT NULL DEFAULT '',
+ gender gender NOT NULL DEFAULT 'unknown',
+ s_bust smallint NOT NULL DEFAULT 0,
+ s_waist smallint NOT NULL DEFAULT 0,
+ s_hip smallint NOT NULL DEFAULT 0,
+ b_month smallint NOT NULL DEFAULT 0,
+ b_day smallint NOT NULL DEFAULT 0,
+ height smallint NOT NULL DEFAULT 0,
+ weight smallint NOT NULL DEFAULT 0,
+ bloodt blood_type NOT NULL DEFAULT 'unknown',
+ main integer, -- chars.id
+ main_spoil smallint NOT NULL DEFAULT 0
);
--- chars_rev
-CREATE TABLE chars_rev (
- id integer NOT NULL PRIMARY KEY,
- cid integer NOT NULL,
+-- chars_hist
+CREATE TABLE chars_hist (
+ chid integer NOT NULL PRIMARY KEY,
name varchar(250) NOT NULL DEFAULT '',
original varchar(250) NOT NULL DEFAULT '',
alias varchar(500) NOT NULL DEFAULT '',
@@ -63,25 +119,42 @@ CREATE TABLE chars_rev (
height smallint NOT NULL DEFAULT 0,
weight smallint NOT NULL DEFAULT 0,
bloodt blood_type NOT NULL DEFAULT 'unknown',
- main integer,
+ main integer, -- chars.id
main_spoil smallint NOT NULL DEFAULT 0
);
-- chars_traits
CREATE TABLE chars_traits (
- cid integer NOT NULL,
- tid integer NOT NULL,
- spoil smallint NOT NULL DEFAULT 0,
- PRIMARY KEY(cid, tid)
+ id integer NOT NULL,
+ tid integer NOT NULL, -- traits.id
+ spoil smallint NOT NULL DEFAULT 0,
+ PRIMARY KEY(id, tid)
+);
+
+-- chars_traits_hist
+CREATE TABLE chars_traits_hist (
+ chid integer NOT NULL,
+ tid integer NOT NULL, -- traits.id
+ spoil smallint NOT NULL DEFAULT 0,
+ PRIMARY KEY(chid, tid)
);
-- chars_vns
CREATE TABLE chars_vns (
- cid integer NOT NULL,
- vid integer NOT NULL,
- rid integer NULL,
- spoil smallint NOT NULL DEFAULT 0,
- role char_role NOT NULL DEFAULT 'main'
+ id integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ rid integer NULL, -- releases.id
+ spoil smallint NOT NULL DEFAULT 0,
+ role char_role NOT NULL DEFAULT 'main'
+);
+
+-- chars_vns_hist
+CREATE TABLE chars_vns_hist (
+ chid integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ rid integer NULL, -- releases.id
+ spoil smallint NOT NULL DEFAULT 0,
+ role char_role NOT NULL DEFAULT 'main'
);
-- login_throttle
@@ -105,34 +178,48 @@ CREATE TABLE notifications (
);
-- producers
-CREATE TABLE producers (
- id SERIAL NOT NULL PRIMARY KEY,
- latest integer NOT NULL DEFAULT 0,
- locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE,
- rgraph integer
+CREATE TABLE producers ( -- dbentry_type=p
+ id SERIAL PRIMARY KEY,
+ locked boolean NOT NULL DEFAULT FALSE,
+ hidden boolean NOT NULL DEFAULT FALSE,
+ type producer_type NOT NULL DEFAULT 'co',
+ name varchar(200) NOT NULL DEFAULT '',
+ original varchar(200) NOT NULL DEFAULT '',
+ website varchar(250) NOT NULL DEFAULT '',
+ lang language NOT NULL DEFAULT 'ja',
+ "desc" text NOT NULL DEFAULT '',
+ alias varchar(500) NOT NULL DEFAULT '',
+ l_wp varchar(150),
+ rgraph integer -- relgraphs.id
+);
+
+-- producers_hist
+CREATE TABLE producers_hist (
+ chid integer NOT NULL PRIMARY KEY,
+ type producer_type NOT NULL DEFAULT 'co',
+ name varchar(200) NOT NULL DEFAULT '',
+ original varchar(200) NOT NULL DEFAULT '',
+ website varchar(250) NOT NULL DEFAULT '',
+ lang language NOT NULL DEFAULT 'ja',
+ "desc" text NOT NULL DEFAULT '',
+ alias varchar(500) NOT NULL DEFAULT '',
+ l_wp varchar(150)
);
-- producers_relations
CREATE TABLE producers_relations (
- pid1 integer NOT NULL,
- pid2 integer NOT NULL,
- relation producer_relation NOT NULL,
- PRIMARY KEY(pid1, pid2)
+ id integer NOT NULL,
+ pid integer NOT NULL, -- producers.id
+ relation producer_relation NOT NULL,
+ PRIMARY KEY(id, pid)
);
--- producers_rev
-CREATE TABLE producers_rev (
- id integer NOT NULL PRIMARY KEY,
- pid integer NOT NULL DEFAULT 0,
- type producer_type NOT NULL DEFAULT 'co',
- name varchar(200) NOT NULL DEFAULT '',
- original varchar(200) NOT NULL DEFAULT '',
- website varchar(250) NOT NULL DEFAULT '',
- lang language NOT NULL DEFAULT 'ja',
- "desc" text NOT NULL DEFAULT '',
- alias varchar(500) NOT NULL DEFAULT '',
- l_wp varchar(150)
+-- producers_relations_hist
+CREATE TABLE producers_relations_hist (
+ chid integer NOT NULL,
+ pid integer NOT NULL, -- producers.id
+ relation producer_relation NOT NULL,
+ PRIMARY KEY(chid, pid)
);
-- quotes
@@ -143,72 +230,125 @@ CREATE TABLE quotes (
);
-- releases
-CREATE TABLE releases (
- id SERIAL NOT NULL PRIMARY KEY,
- latest integer NOT NULL DEFAULT 0,
- locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE
+CREATE TABLE releases ( -- dbentry_type=r
+ id SERIAL PRIMARY KEY,
+ locked boolean NOT NULL DEFAULT FALSE,
+ hidden boolean NOT NULL DEFAULT FALSE,
+ title varchar(250) NOT NULL DEFAULT '',
+ original varchar(250) NOT NULL DEFAULT '',
+ type release_type NOT NULL DEFAULT 'complete',
+ website varchar(250) NOT NULL DEFAULT '',
+ catalog varchar(50) NOT NULL DEFAULT '',
+ gtin bigint NOT NULL DEFAULT 0,
+ released integer NOT NULL DEFAULT 0,
+ notes text NOT NULL DEFAULT '',
+ minage smallint,
+ patch boolean NOT NULL DEFAULT FALSE,
+ freeware boolean NOT NULL DEFAULT FALSE,
+ doujin boolean NOT NULL DEFAULT FALSE,
+ resolution smallint NOT NULL DEFAULT 0,
+ voiced smallint NOT NULL DEFAULT 0,
+ ani_story smallint NOT NULL DEFAULT 0,
+ ani_ero smallint NOT NULL DEFAULT 0
+);
+
+-- releases_hist
+CREATE TABLE releases_hist (
+ chid integer NOT NULL PRIMARY KEY,
+ title varchar(250) NOT NULL DEFAULT '',
+ original varchar(250) NOT NULL DEFAULT '',
+ type release_type NOT NULL DEFAULT 'complete',
+ website varchar(250) NOT NULL DEFAULT '',
+ catalog varchar(50) NOT NULL DEFAULT '',
+ gtin bigint NOT NULL DEFAULT 0,
+ released integer NOT NULL DEFAULT 0,
+ notes text NOT NULL DEFAULT '',
+ minage smallint,
+ patch boolean NOT NULL DEFAULT FALSE,
+ freeware boolean NOT NULL DEFAULT FALSE,
+ doujin boolean NOT NULL DEFAULT FALSE,
+ resolution smallint NOT NULL DEFAULT 0,
+ voiced smallint NOT NULL DEFAULT 0,
+ ani_story smallint NOT NULL DEFAULT 0,
+ ani_ero smallint NOT NULL DEFAULT 0
);
-- releases_lang
CREATE TABLE releases_lang (
- rid integer NOT NULL,
- lang language NOT NULL,
- PRIMARY KEY(rid, lang)
+ id integer NOT NULL,
+ lang language NOT NULL,
+ PRIMARY KEY(id, lang)
+);
+
+-- releases_lang_hist
+CREATE TABLE releases_lang_hist (
+ chid integer NOT NULL,
+ lang language NOT NULL,
+ PRIMARY KEY(chid, lang)
);
-- releases_media
CREATE TABLE releases_media (
- rid integer NOT NULL DEFAULT 0,
- medium medium NOT NULL,
- qty smallint NOT NULL DEFAULT 1,
- PRIMARY KEY(rid, medium, qty)
+ id integer NOT NULL,
+ medium medium NOT NULL,
+ qty smallint NOT NULL DEFAULT 1,
+ PRIMARY KEY(id, medium, qty)
+);
+
+-- releases_media_hist
+CREATE TABLE releases_media_hist (
+ chid integer NOT NULL,
+ medium medium NOT NULL,
+ qty smallint NOT NULL DEFAULT 1,
+ PRIMARY KEY(chid, medium, qty)
);
-- releases_platforms
CREATE TABLE releases_platforms (
- rid integer NOT NULL DEFAULT 0,
- platform platform NOT NULL,
- PRIMARY KEY(rid, platform)
+ id integer NOT NULL,
+ platform platform NOT NULL,
+ PRIMARY KEY(id, platform)
+);
+
+-- releases_platforms_hist
+CREATE TABLE releases_platforms_hist (
+ chid integer NOT NULL,
+ platform platform NOT NULL,
+ PRIMARY KEY(chid, platform)
);
-- releases_producers
CREATE TABLE releases_producers (
- pid integer NOT NULL,
- rid integer NOT NULL,
- developer boolean NOT NULL DEFAULT FALSE,
- publisher boolean NOT NULL DEFAULT TRUE,
+ id integer NOT NULL,
+ pid integer NOT NULL, -- producers.id
+ developer boolean NOT NULL DEFAULT FALSE,
+ publisher boolean NOT NULL DEFAULT TRUE,
CHECK(developer OR publisher),
- PRIMARY KEY(pid, rid)
+ PRIMARY KEY(id, pid)
);
--- releases_rev
-CREATE TABLE releases_rev (
- id integer NOT NULL PRIMARY KEY,
- rid integer NOT NULL DEFAULT 0,
- title varchar(250) NOT NULL DEFAULT '',
- original varchar(250) NOT NULL DEFAULT '',
- type release_type NOT NULL DEFAULT 'complete',
- website varchar(250) NOT NULL DEFAULT '',
- released integer NOT NULL DEFAULT 0,
- notes text NOT NULL DEFAULT '',
- minage smallint,
- gtin bigint NOT NULL DEFAULT 0,
- patch boolean NOT NULL DEFAULT FALSE,
- catalog varchar(50) NOT NULL DEFAULT '',
- resolution smallint NOT NULL DEFAULT 0,
- voiced smallint NOT NULL DEFAULT 0,
- freeware boolean NOT NULL DEFAULT FALSE,
- doujin boolean NOT NULL DEFAULT FALSE,
- ani_story smallint NOT NULL DEFAULT 0,
- ani_ero smallint NOT NULL DEFAULT 0
+-- releases_producers_hist
+CREATE TABLE releases_producers_hist (
+ chid integer NOT NULL,
+ pid integer NOT NULL, -- producers.id
+ developer boolean NOT NULL DEFAULT FALSE,
+ publisher boolean NOT NULL DEFAULT TRUE,
+ CHECK(developer OR publisher),
+ PRIMARY KEY(chid, pid)
);
-- releases_vn
CREATE TABLE releases_vn (
- rid integer NOT NULL DEFAULT 0,
- vid integer NOT NULL DEFAULT 0,
- PRIMARY KEY(rid, vid)
+ id integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ PRIMARY KEY(id, vid)
+);
+
+-- releases_vn_hist
+CREATE TABLE releases_vn_hist (
+ chid integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ PRIMARY KEY(chid, vid)
);
-- relgraphs
@@ -243,34 +383,48 @@ CREATE TABLE sessions (
);
-- staff
-CREATE TABLE staff (
- id SERIAL NOT NULL PRIMARY KEY,
- latest integer NOT NULL DEFAULT 0,
- locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE
+CREATE TABLE staff ( -- dbentry_type=s
+ id SERIAL PRIMARY KEY,
+ locked boolean NOT NULL DEFAULT FALSE,
+ hidden boolean NOT NULL DEFAULT FALSE,
+ aid integer NOT NULL DEFAULT 0, -- staff_alias.aid
+ gender gender NOT NULL DEFAULT 'unknown',
+ lang language NOT NULL DEFAULT 'ja',
+ "desc" text NOT NULL DEFAULT '',
+ l_wp varchar(150) NOT NULL DEFAULT '',
+ l_site varchar(250) NOT NULL DEFAULT '',
+ l_twitter varchar(16) NOT NULL DEFAULT '',
+ l_anidb integer
+);
+
+-- staff_hist
+CREATE TABLE staff_hist (
+ chid integer NOT NULL PRIMARY KEY,
+ aid integer NOT NULL DEFAULT 0, -- Can't refer to staff_alias.id, because the alias might have been deleted
+ gender gender NOT NULL DEFAULT 'unknown',
+ lang language NOT NULL DEFAULT 'ja',
+ "desc" text NOT NULL DEFAULT '',
+ l_wp varchar(150) NOT NULL DEFAULT '',
+ l_site varchar(250) NOT NULL DEFAULT '',
+ l_twitter varchar(16) NOT NULL DEFAULT '',
+ l_anidb integer
);
-- staff_alias
CREATE TABLE staff_alias (
- id SERIAL NOT NULL,
- rid integer,
- name varchar(200) NOT NULL DEFAULT '',
- original varchar(200) NOT NULL DEFAULT '',
- PRIMARY KEY (id, rid)
+ id integer NOT NULL,
+ aid SERIAL PRIMARY KEY, -- Globally unique ID of this alias
+ name varchar(200) NOT NULL DEFAULT '',
+ original varchar(200) NOT NULL DEFAULT ''
);
--- staff_rev
-CREATE TABLE staff_rev (
- id integer NOT NULL PRIMARY KEY,
- sid integer NOT NULL,
- aid integer NOT NULL,
- gender gender NOT NULL DEFAULT 'unknown',
- lang language NOT NULL DEFAULT 'ja',
- "desc" text NOT NULL DEFAULT '',
- l_wp varchar(150) NOT NULL DEFAULT '',
- l_site varchar(250) NOT NULL DEFAULT '',
- l_twitter varchar(16) NOT NULL DEFAULT '',
- l_anidb integer
+-- staff_alias_hist
+CREATE TABLE staff_alias_hist (
+ chid integer NOT NULL,
+ aid integer NOT NULL, -- staff_alias.aid, but can't reference it because the alias may have been deleted
+ name varchar(200) NOT NULL DEFAULT '',
+ original varchar(200) NOT NULL DEFAULT '',
+ PRIMARY KEY(chid, aid)
);
-- stats_cache
@@ -397,9 +551,6 @@ CREATE TABLE users (
-- First 9 bytes: salt (ASCII)
-- Latter 20 bytes: sha1(hex(token) + salt)
-- 'token' is a sha1 digest obtained from random data.
- -- * 41 bytes: sha256 password
- -- First 9 bytes: salt (ASCII)
- -- Latter 32 bytes: sha256(global_salt + password + salt)
-- * 46 bytes: scrypt password
-- 4 bytes: N (big endian)
-- 1 byte: r
@@ -426,79 +577,130 @@ CREATE TABLE users_prefs (
);
-- vn
-CREATE TABLE vn (
- id SERIAL NOT NULL PRIMARY KEY,
- latest integer NOT NULL DEFAULT 0,
- locked boolean NOT NULL DEFAULT FALSE,
- hidden boolean NOT NULL DEFAULT FALSE,
- rgraph integer,
+CREATE TABLE vn ( -- dbentry_type=v
+ id SERIAL PRIMARY KEY,
+ locked boolean NOT NULL DEFAULT FALSE,
+ hidden boolean NOT NULL DEFAULT FALSE,
+ title varchar(250) NOT NULL DEFAULT '',
+ original varchar(250) NOT NULL DEFAULT '',
+ alias varchar(500) NOT NULL DEFAULT '',
+ length smallint NOT NULL DEFAULT 0,
+ img_nsfw boolean NOT NULL DEFAULT FALSE,
+ image integer NOT NULL DEFAULT 0,
+ "desc" text NOT NULL DEFAULT '',
+ l_wp varchar(150) NOT NULL DEFAULT '',
+ l_encubed varchar(100) NOT NULL DEFAULT '',
+ l_renai varchar(100) NOT NULL DEFAULT '',
+ rgraph integer, -- relgraphs.id
c_released integer NOT NULL DEFAULT 0,
c_languages language[] NOT NULL DEFAULT '{}',
+ c_olang language[] NOT NULL DEFAULT '{}',
c_platforms platform[] NOT NULL DEFAULT '{}',
c_popularity real,
- c_rating real,
+ c_rating real,
c_votecount integer NOT NULL DEFAULT 0,
- c_search text,
- c_olang language[] NOT NULL DEFAULT '{}'
+ c_search text
+);
+
+-- vn_hist
+CREATE TABLE vn_hist (
+ chid integer NOT NULL PRIMARY KEY,
+ title varchar(250) NOT NULL DEFAULT '',
+ original varchar(250) NOT NULL DEFAULT '',
+ alias varchar(500) NOT NULL DEFAULT '',
+ length smallint NOT NULL DEFAULT 0,
+ img_nsfw boolean NOT NULL DEFAULT FALSE,
+ image integer NOT NULL DEFAULT 0,
+ "desc" text NOT NULL DEFAULT '',
+ l_wp varchar(150) NOT NULL DEFAULT '',
+ l_encubed varchar(100) NOT NULL DEFAULT '',
+ l_renai varchar(100) NOT NULL DEFAULT ''
);
-- vn_anime
CREATE TABLE vn_anime (
- vid integer NOT NULL,
- aid integer NOT NULL,
- PRIMARY KEY(vid, aid)
+ id integer NOT NULL,
+ aid integer NOT NULL, -- anime.id
+ PRIMARY KEY(id, aid)
+);
+
+-- vn_anime_hist
+CREATE TABLE vn_anime_hist (
+ chid integer NOT NULL,
+ aid integer NOT NULL, -- anime.id
+ PRIMARY KEY(chid, aid)
);
-- vn_relations
CREATE TABLE vn_relations (
- vid1 integer NOT NULL DEFAULT 0,
- vid2 integer NOT NULL DEFAULT 0,
- relation vn_relation NOT NULL,
- official boolean NOT NULL DEFAULT TRUE,
- PRIMARY KEY(vid1, vid2)
+ id integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ relation vn_relation NOT NULL,
+ official boolean NOT NULL DEFAULT TRUE,
+ PRIMARY KEY(id, vid)
);
--- vn_rev
-CREATE TABLE vn_rev (
- id integer NOT NULL PRIMARY KEY,
- vid integer NOT NULL DEFAULT 0,
- title varchar(250) NOT NULL DEFAULT '',
- alias varchar(500) NOT NULL DEFAULT '',
- img_nsfw boolean NOT NULL DEFAULT FALSE,
- length smallint NOT NULL DEFAULT 0,
- "desc" text NOT NULL DEFAULT '',
- l_wp varchar(150) NOT NULL DEFAULT '',
- image integer NOT NULL DEFAULT 0,
- l_encubed varchar(100) NOT NULL DEFAULT '',
- l_renai varchar(100) NOT NULL DEFAULT '',
- original varchar(250) NOT NULL DEFAULT ''
+-- vn_relations_hist
+CREATE TABLE vn_relations_hist (
+ chid integer NOT NULL,
+ vid integer NOT NULL, -- vn.id
+ relation vn_relation NOT NULL,
+ official boolean NOT NULL DEFAULT TRUE,
+ PRIMARY KEY(chid, vid)
);
-- vn_screenshots
CREATE TABLE vn_screenshots (
- vid integer NOT NULL DEFAULT 0,
- scr integer NOT NULL DEFAULT 0,
- nsfw boolean NOT NULL DEFAULT FALSE,
- rid integer,
- PRIMARY KEY(vid, scr)
+ id integer NOT NULL,
+ scr integer NOT NULL, -- screenshots.id
+ rid integer, -- releases.id (only NULL for old revisions, nowadays not allowed anymore)
+ nsfw boolean NOT NULL DEFAULT FALSE,
+ PRIMARY KEY(id, scr)
+);
+
+-- vn_screenshots_hist
+CREATE TABLE vn_screenshots_hist (
+ chid integer NOT NULL,
+ scr integer NOT NULL,
+ rid integer,
+ nsfw boolean NOT NULL DEFAULT FALSE,
+ PRIMARY KEY(chid, scr)
);
-- vn_seiyuu
CREATE TABLE vn_seiyuu (
- vid integer NOT NULL,
- aid integer NOT NULL,
- cid integer NOT NULL,
- note varchar(250) NOT NULL DEFAULT '',
- PRIMARY KEY (vid, aid, cid)
+ id integer NOT NULL,
+ aid integer NOT NULL, -- staff_alias.aid
+ cid integer NOT NULL, -- chars.id
+ note varchar(250) NOT NULL DEFAULT '',
+ PRIMARY KEY (id, aid, cid)
+);
+
+-- vn_seiyuu_hist
+CREATE TABLE vn_seiyuu_hist (
+ chid integer NOT NULL,
+ aid integer NOT NULL, -- staff_alias.aid, but can't reference it because the alias may have been deleted
+ cid integer NOT NULL, -- chars.id
+ note varchar(250) NOT NULL DEFAULT '',
+ PRIMARY KEY (chid, aid, cid)
);
-- vn_staff
CREATE TABLE vn_staff (
- vid integer NOT NULL,
- aid integer NOT NULL,
- role credit_type NOT NULL DEFAULT 'staff',
- note varchar(250) NOT NULL DEFAULT '',
- PRIMARY KEY (vid, aid, role)
+ id integer NOT NULL,
+ aid integer NOT NULL, -- staff_alias.aid
+ role credit_type NOT NULL DEFAULT 'staff',
+ note varchar(250) NOT NULL DEFAULT '',
+ PRIMARY KEY (id, aid, role)
+);
+
+-- vn_staff_hist
+CREATE TABLE vn_staff_hist (
+ chid integer NOT NULL,
+ aid integer NOT NULL, -- See note at vn_seiyuu_hist.aid
+ role credit_type NOT NULL DEFAULT 'staff',
+ note varchar(250) NOT NULL DEFAULT '',
+ PRIMARY KEY (chid, aid, role)
);
-- vnlists
diff --git a/util/sql/tableattrs.sql b/util/sql/tableattrs.sql
index 4028e98e..7a144e1f 100644
--- a/util/sql/tableattrs.sql
+++ b/util/sql/tableattrs.sql
@@ -1,88 +1,117 @@
-ALTER TABLE affiliate_links ADD CONSTRAINT affiliate_links_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
-ALTER TABLE changes ADD CONSTRAINT changes_requester_fkey FOREIGN KEY (requester) REFERENCES users (id) ON DELETE SET DEFAULT;
-ALTER TABLE chars ADD CONSTRAINT chars_latest_fkey FOREIGN KEY (latest) REFERENCES chars_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE chars_rev ADD CONSTRAINT chars_rev_id_fkey FOREIGN KEY (id) REFERENCES changes (id);
-ALTER TABLE chars_rev ADD CONSTRAINT chars_rev_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
-ALTER TABLE chars_rev ADD CONSTRAINT chars_rev_main_fkey FOREIGN KEY (main) REFERENCES chars (id);
-ALTER TABLE chars_traits ADD CONSTRAINT chars_traits_cid_fkey FOREIGN KEY (cid) REFERENCES chars_rev (id);
-ALTER TABLE chars_traits ADD CONSTRAINT chars_traits_tid_fkey FOREIGN KEY (tid) REFERENCES traits (id);
-ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_cid_fkey FOREIGN KEY (cid) REFERENCES chars_rev (id);
-ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
-ALTER TABLE notifications ADD CONSTRAINT notifications_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE notifications ADD CONSTRAINT notifications_c_byuser_fkey FOREIGN KEY (c_byuser) REFERENCES users (id) ON DELETE SET DEFAULT;
-ALTER TABLE producers ADD CONSTRAINT producers_latest_fkey FOREIGN KEY (latest) REFERENCES producers_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE producers ADD CONSTRAINT producers_rgraph_fkey FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
-ALTER TABLE producers_relations ADD CONSTRAINT producers_relations_pid1_fkey FOREIGN KEY (pid1) REFERENCES producers_rev (id);
-ALTER TABLE producers_relations ADD CONSTRAINT producers_relations_pid2_fkey FOREIGN KEY (pid2) REFERENCES producers (id);
-ALTER TABLE producers_rev ADD CONSTRAINT producers_rev_id_fkey FOREIGN KEY (id) REFERENCES changes (id);
-ALTER TABLE producers_rev ADD CONSTRAINT producers_rev_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
-ALTER TABLE quotes ADD CONSTRAINT quotes_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE releases ADD CONSTRAINT releases_latest_fkey FOREIGN KEY (latest) REFERENCES releases_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE releases_lang ADD CONSTRAINT releases_lang_rid_fkey FOREIGN KEY (rid) REFERENCES releases_rev (id);
-ALTER TABLE releases_media ADD CONSTRAINT releases_media_rid_fkey FOREIGN KEY (rid) REFERENCES releases_rev (id);
-ALTER TABLE releases_platforms ADD CONSTRAINT releases_platforms_rid_fkey FOREIGN KEY (rid) REFERENCES releases_rev (id);
-ALTER TABLE releases_producers ADD CONSTRAINT releases_producers_rid_fkey FOREIGN KEY (rid) REFERENCES releases_rev (id);
-ALTER TABLE releases_producers ADD CONSTRAINT releases_producers_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
-ALTER TABLE releases_rev ADD CONSTRAINT releases_rev_id_fkey FOREIGN KEY (id) REFERENCES changes (id);
-ALTER TABLE releases_rev ADD CONSTRAINT releases_rev_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
-ALTER TABLE releases_vn ADD CONSTRAINT releases_vn_rid_fkey FOREIGN KEY (rid) REFERENCES releases_rev (id);
-ALTER TABLE releases_vn ADD CONSTRAINT releases_vn_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE rlists ADD CONSTRAINT rlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE rlists ADD CONSTRAINT rlists_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
-ALTER TABLE sessions ADD CONSTRAINT sessions_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE staff ADD CONSTRAINT staff_latest_fkey FOREIGN KEY (latest) REFERENCES staff_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE staff_alias ADD CONSTRAINT staff_alias_rid_fkey FOREIGN KEY (rid) REFERENCES staff_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE staff_rev ADD CONSTRAINT staff_rev_id_fkey FOREIGN KEY (id) REFERENCES changes (id);
-ALTER TABLE staff_rev ADD CONSTRAINT staff_rev_sid_fkey FOREIGN KEY (sid) REFERENCES staff (id);
-ALTER TABLE staff_rev ADD CONSTRAINT staff_rev_aid_fkey FOREIGN KEY (aid,id) REFERENCES staff_alias (id,rid);
-ALTER TABLE tags ADD CONSTRAINT tags_addedby_fkey FOREIGN KEY (addedby) REFERENCES users (id) ON DELETE SET DEFAULT;
-ALTER TABLE tags_aliases ADD CONSTRAINT tags_aliases_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
-ALTER TABLE tags_parents ADD CONSTRAINT tags_parents_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
-ALTER TABLE tags_parents ADD CONSTRAINT tags_parents_parent_fkey FOREIGN KEY (parent) REFERENCES tags (id);
-ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
-ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE threads ADD CONSTRAINT threads_id_fkey FOREIGN KEY (id, count) REFERENCES threads_posts (tid, num) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE threads_posts ADD CONSTRAINT threads_posts_tid_fkey FOREIGN KEY (tid) REFERENCES threads (id);
-ALTER TABLE threads_posts ADD CONSTRAINT threads_posts_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE SET DEFAULT;
-ALTER TABLE threads_boards ADD CONSTRAINT threads_boards_tid_fkey FOREIGN KEY (tid) REFERENCES threads (id);
-ALTER TABLE traits ADD CONSTRAINT traits_addedby_fkey FOREIGN KEY (addedby) REFERENCES users (id);
-ALTER TABLE traits ADD CONSTRAINT traits_group_fkey FOREIGN KEY ("group") REFERENCES traits (id);
-ALTER TABLE traits_parents ADD CONSTRAINT traits_parents_trait_fkey FOREIGN KEY (trait) REFERENCES traits (id);
-ALTER TABLE traits_parents ADD CONSTRAINT traits_parents_parent_fkey FOREIGN KEY (parent) REFERENCES traits (id);
-ALTER TABLE traits_chars ADD CONSTRAINT traits_chars_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
-ALTER TABLE traits_chars ADD CONSTRAINT traits_chars_tid_fkey FOREIGN KEY (tid) REFERENCES traits (id);
-ALTER TABLE users_prefs ADD CONSTRAINT users_prefs_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE vn ADD CONSTRAINT vn_latest_fkey FOREIGN KEY (latest) REFERENCES vn_rev (id) DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE vn ADD CONSTRAINT vn_rgraph_fkey FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
-ALTER TABLE vn_anime ADD CONSTRAINT vn_anime_aid_fkey FOREIGN KEY (aid) REFERENCES anime (id);
-ALTER TABLE vn_anime ADD CONSTRAINT vn_anime_vid_fkey FOREIGN KEY (vid) REFERENCES vn_rev (id);
-ALTER TABLE vn_relations ADD CONSTRAINT vn_relations_vid1_fkey FOREIGN KEY (vid1) REFERENCES vn_rev (id);
-ALTER TABLE vn_relations ADD CONSTRAINT vn_relations_vid2_fkey FOREIGN KEY (vid2) REFERENCES vn (id);
-ALTER TABLE vn_rev ADD CONSTRAINT vn_rev_id_fkey FOREIGN KEY (id) REFERENCES changes (id);
-ALTER TABLE vn_rev ADD CONSTRAINT vn_rev_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_vid_fkey FOREIGN KEY (vid) REFERENCES vn_rev (id);
-ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_scr_fkey FOREIGN KEY (scr) REFERENCES screenshots (id);
-ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
-ALTER TABLE vn_seiyuu ADD CONSTRAINT vn_seiyuu_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
-ALTER TABLE vn_seiyuu ADD CONSTRAINT vn_seiyuu_vid_fkey FOREIGN KEY (vid) REFERENCES vn_rev (id);
-ALTER TABLE vn_staff ADD CONSTRAINT vn_staff_vid_fkey FOREIGN KEY (vid) REFERENCES vn_rev (id);
-ALTER TABLE vnlists ADD CONSTRAINT vnlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE vnlists ADD CONSTRAINT vnlists_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE votes ADD CONSTRAINT votes_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE votes ADD CONSTRAINT votes_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
-ALTER TABLE wlists ADD CONSTRAINT wlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
-ALTER TABLE wlists ADD CONSTRAINT wlists_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE affiliate_links ADD CONSTRAINT affiliate_links_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE changes ADD CONSTRAINT changes_requester_fkey FOREIGN KEY (requester) REFERENCES users (id) ON DELETE SET DEFAULT;
+ALTER TABLE chars ADD CONSTRAINT chars_main_fkey FOREIGN KEY (main) REFERENCES chars (id);
+ALTER TABLE chars_hist ADD CONSTRAINT chars_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE chars_hist ADD CONSTRAINT chars_hist_main_fkey FOREIGN KEY (main) REFERENCES chars (id);
+ALTER TABLE chars_traits ADD CONSTRAINT chars_traits_id_fkey FOREIGN KEY (id) REFERENCES chars (id);
+ALTER TABLE chars_traits ADD CONSTRAINT chars_traits_tid_fkey FOREIGN KEY (tid) REFERENCES traits (id);
+ALTER TABLE chars_traits_hist ADD CONSTRAINT chars_traits_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE chars_traits_hist ADD CONSTRAINT chars_traits_hist_tid_fkey FOREIGN KEY (tid) REFERENCES traits (id);
+ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_id_fkey FOREIGN KEY (id) REFERENCES chars (id);
+ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE chars_vns ADD CONSTRAINT chars_vns_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE chars_vns_hist ADD CONSTRAINT chars_vns_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE chars_vns_hist ADD CONSTRAINT chars_vns_hist_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE chars_vns_hist ADD CONSTRAINT chars_vns_hist_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE notifications ADD CONSTRAINT notifications_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE notifications ADD CONSTRAINT notifications_c_byuser_fkey FOREIGN KEY (c_byuser) REFERENCES users (id) ON DELETE SET DEFAULT;
+ALTER TABLE producers ADD CONSTRAINT producers_rgraph_fkey FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
+ALTER TABLE producers_hist ADD CONSTRAINT producers_chid_id_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE producers_relations ADD CONSTRAINT producers_relations_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
+ALTER TABLE producers_relations_hist ADD CONSTRAINT producers_relations_hist_id_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE producers_relations_hist ADD CONSTRAINT producers_relations_hist_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
+ALTER TABLE quotes ADD CONSTRAINT quotes_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE releases_hist ADD CONSTRAINT releases_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_lang ADD CONSTRAINT releases_lang_id_fkey FOREIGN KEY (id) REFERENCES releases (id);
+ALTER TABLE releases_lang_hist ADD CONSTRAINT releases_lang_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_media ADD CONSTRAINT releases_media_id_fkey FOREIGN KEY (id) REFERENCES releases (id);
+ALTER TABLE releases_media_hist ADD CONSTRAINT releases_media_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_platforms ADD CONSTRAINT releases_platforms_id_fkey FOREIGN KEY (id) REFERENCES releases (id);
+ALTER TABLE releases_platforms_hist ADD CONSTRAINT releases_platforms_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_producers ADD CONSTRAINT releases_producers_id_fkey FOREIGN KEY (id) REFERENCES releases (id);
+ALTER TABLE releases_producers ADD CONSTRAINT releases_producers_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
+ALTER TABLE releases_producers_hist ADD CONSTRAINT releases_producers_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_producers_hist ADD CONSTRAINT releases_producers_hist_pid_fkey FOREIGN KEY (pid) REFERENCES producers (id);
+ALTER TABLE releases_vn ADD CONSTRAINT releases_vn_id_fkey FOREIGN KEY (id) REFERENCES releases (id);
+ALTER TABLE releases_vn ADD CONSTRAINT releases_vn_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE releases_vn_hist ADD CONSTRAINT releases_vn_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE releases_vn_hist ADD CONSTRAINT releases_vn_hist_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE rlists ADD CONSTRAINT rlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE rlists ADD CONSTRAINT rlists_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE sessions ADD CONSTRAINT sessions_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE staff ADD CONSTRAINT staff_aid_fkey FOREIGN KEY (aid) REFERENCES staff_alias (aid) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE staff_hist ADD CONSTRAINT staff_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE staff_alias ADD CONSTRAINT staff_alias_id_fkey FOREIGN KEY (id) REFERENCES staff (id);
+ALTER TABLE staff_alias_hist ADD CONSTRAINT staff_alias_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE tags ADD CONSTRAINT tags_addedby_fkey FOREIGN KEY (addedby) REFERENCES users (id) ON DELETE SET DEFAULT;
+ALTER TABLE tags_aliases ADD CONSTRAINT tags_aliases_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
+ALTER TABLE tags_parents ADD CONSTRAINT tags_parents_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
+ALTER TABLE tags_parents ADD CONSTRAINT tags_parents_parent_fkey FOREIGN KEY (parent) REFERENCES tags (id);
+ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_tag_fkey FOREIGN KEY (tag) REFERENCES tags (id);
+ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE tags_vn ADD CONSTRAINT tags_vn_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE threads ADD CONSTRAINT threads_id_fkey FOREIGN KEY (id, count) REFERENCES threads_posts (tid, num) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE threads_posts ADD CONSTRAINT threads_posts_tid_fkey FOREIGN KEY (tid) REFERENCES threads (id);
+ALTER TABLE threads_posts ADD CONSTRAINT threads_posts_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE SET DEFAULT;
+ALTER TABLE threads_boards ADD CONSTRAINT threads_boards_tid_fkey FOREIGN KEY (tid) REFERENCES threads (id);
+ALTER TABLE traits ADD CONSTRAINT traits_addedby_fkey FOREIGN KEY (addedby) REFERENCES users (id);
+ALTER TABLE traits ADD CONSTRAINT traits_group_fkey FOREIGN KEY ("group") REFERENCES traits (id);
+ALTER TABLE traits_parents ADD CONSTRAINT traits_parents_trait_fkey FOREIGN KEY (trait) REFERENCES traits (id);
+ALTER TABLE traits_parents ADD CONSTRAINT traits_parents_parent_fkey FOREIGN KEY (parent) REFERENCES traits (id);
+ALTER TABLE traits_chars ADD CONSTRAINT traits_chars_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
+ALTER TABLE traits_chars ADD CONSTRAINT traits_chars_tid_fkey FOREIGN KEY (tid) REFERENCES traits (id);
+ALTER TABLE users_prefs ADD CONSTRAINT users_prefs_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE vn ADD CONSTRAINT vn_rgraph_fkey FOREIGN KEY (rgraph) REFERENCES relgraphs (id);
+ALTER TABLE vn_hist ADD CONSTRAINT vn_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vn_anime ADD CONSTRAINT vn_anime_id_fkey FOREIGN KEY (id) REFERENCES vn (id);
+ALTER TABLE vn_anime ADD CONSTRAINT vn_anime_aid_fkey FOREIGN KEY (aid) REFERENCES anime (id);
+ALTER TABLE vn_anime_hist ADD CONSTRAINT vn_anime_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vn_anime_hist ADD CONSTRAINT vn_anime_hist_aid_fkey FOREIGN KEY (aid) REFERENCES anime (id);
+ALTER TABLE vn_relations ADD CONSTRAINT vn_relations_id_fkey FOREIGN KEY (id) REFERENCES vn (id);
+ALTER TABLE vn_relations ADD CONSTRAINT vn_relations_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE vn_relations_hist ADD CONSTRAINT vn_relations_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vn_relations_hist ADD CONSTRAINT vn_relations_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_id_fkey FOREIGN KEY (id) REFERENCES vn (id);
+ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_scr_fkey FOREIGN KEY (scr) REFERENCES screenshots (id);
+ALTER TABLE vn_screenshots ADD CONSTRAINT vn_screenshots_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE vn_screenshots_hist ADD CONSTRAINT vn_screenshots_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vn_screenshots_hist ADD CONSTRAINT vn_screenshots_hist_scr_fkey FOREIGN KEY (scr) REFERENCES screenshots (id);
+ALTER TABLE vn_screenshots_hist ADD CONSTRAINT vn_screenshots_hist_rid_fkey FOREIGN KEY (rid) REFERENCES releases (id);
+ALTER TABLE vn_seiyuu ADD CONSTRAINT vn_seiyuu_id_fkey FOREIGN KEY (id) REFERENCES vn (id);
+ALTER TABLE vn_seiyuu ADD CONSTRAINT vn_seiyuu_aid_fkey FOREIGN KEY (aid) REFERENCES staff_alias (aid) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE vn_seiyuu ADD CONSTRAINT vn_seiyuu_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
+ALTER TABLE vn_seiyuu_hist ADD CONSTRAINT vn_seiyuu_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vn_seiyuu_hist ADD CONSTRAINT vn_seiyuu_hist_cid_fkey FOREIGN KEY (cid) REFERENCES chars (id);
+ALTER TABLE vn_staff ADD CONSTRAINT vn_staff_id_fkey FOREIGN KEY (id) REFERENCES vn (id);
+ALTER TABLE vn_staff ADD CONSTRAINT vn_staff_aid_fkey FOREIGN KEY (aid) REFERENCES staff_alias (aid) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE vn_staff_hist ADD CONSTRAINT vn_staff_hist_chid_fkey FOREIGN KEY (chid) REFERENCES changes (id);
+ALTER TABLE vnlists ADD CONSTRAINT vnlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE vnlists ADD CONSTRAINT vnlists_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE votes ADD CONSTRAINT votes_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE votes ADD CONSTRAINT votes_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
+ALTER TABLE wlists ADD CONSTRAINT wlists_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE CASCADE;
+ALTER TABLE wlists ADD CONSTRAINT wlists_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id);
CREATE INDEX affiliate_links_rid ON affiliate_links (rid) WHERE NOT hidden;
+CREATE INDEX chars_main ON chars (main) WHERE main IS NOT NULL AND NOT hidden; -- Only used on /c+
+CREATE INDEX chars_vns_vid ON chars_vns (vid);
CREATE INDEX notifications_uid ON notifications (uid);
-CREATE INDEX releases_producers_rid ON releases_producers (rid);
+CREATE INDEX releases_producers_pid ON releases_producers (pid);
CREATE INDEX releases_vn_vid ON releases_vn (vid);
+CREATE INDEX staff_alias_id ON staff_alias (id);
CREATE INDEX tags_vn_date ON tags_vn (date);
+CREATE INDEX tags_vn_inherit_tag_vid ON tags_vn_inherit (tag, vid);
+CREATE INDEX tags_vn_uid ON tags_vn (uid);
CREATE INDEX tags_vn_vid ON tags_vn (vid);
-CREATE INDEX threads_posts_ts ON threads_posts USING gin(to_tsvector('english', strip_bb_tags(msg)));
-CREATE INDEX vn_staff_vid ON vn_staff (vid);
+CREATE INDEX threads_posts_date ON threads_posts (date);
+CREATE INDEX threads_posts_ts ON threads_posts USING gin(bb_tsvector(msg));
+CREATE INDEX threads_posts_uid ON threads_posts (uid); -- Only really used on /u+ pages to get stats
+CREATE INDEX vn_seiyuu_aid ON vn_seiyuu (aid); -- Only used on /s+?
+CREATE INDEX vn_seiyuu_cid ON vn_seiyuu (cid); -- Only used on /c+?
CREATE INDEX vn_staff_aid ON vn_staff (aid);
-CREATE UNIQUE INDEX chars_vns_pkey ON chars_vns (cid, vid, COALESCE(rid, 0));
+CREATE INDEX votes_date ON votes (date desc); -- Mainly used on /v+ pages, other pages don't really need it
+CREATE INDEX votes_uid ON votes (uid);
+CREATE UNIQUE INDEX changes_itemrev ON changes (type, itemid, rev);
+CREATE UNIQUE INDEX chars_vns_pkey ON chars_vns (id, vid, COALESCE(rid, 0));
+CREATE UNIQUE INDEX chars_vns_hist_pkey ON chars_vns_hist (chid, vid, COALESCE(rid, 0));
diff --git a/util/sql/triggers.sql b/util/sql/triggers.sql
index 6988a62a..962610a7 100644
--- a/util/sql/triggers.sql
+++ b/util/sql/triggers.sql
@@ -1,9 +1,3 @@
-CREATE TRIGGER hidlock_update BEFORE UPDATE ON vn FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
-CREATE TRIGGER hidlock_update BEFORE UPDATE ON producers FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
-CREATE TRIGGER hidlock_update BEFORE UPDATE ON releases FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
-CREATE TRIGGER hidlock_update BEFORE UPDATE ON chars FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
-CREATE TRIGGER hidlock_update BEFORE UPDATE ON staff FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest) EXECUTE PROCEDURE update_hidlock();
-
CREATE TRIGGER users_changes_update AFTER INSERT OR DELETE ON changes FOR EACH ROW EXECUTE PROCEDURE update_users_cache();
CREATE TRIGGER users_votes_update AFTER INSERT OR DELETE ON votes FOR EACH ROW EXECUTE PROCEDURE update_users_cache();
CREATE TRIGGER users_tags_update AFTER INSERT OR DELETE ON tags_vn FOR EACH ROW EXECUTE PROCEDURE update_users_cache();
@@ -34,48 +28,21 @@ CREATE TRIGGER vn_anime_aid_edit BEFORE UPDATE ON vn_anime
CREATE TRIGGER anime_fetch_notify AFTER INSERT OR UPDATE ON anime FOR EACH ROW WHEN (NEW.lastfetch IS NULL) EXECUTE PROCEDURE anime_fetch_notify();
CREATE TRIGGER vn_relgraph_notify AFTER UPDATE ON vn FOR EACH ROW
- WHEN (OLD.rgraph IS DISTINCT FROM NEW.rgraph
- OR OLD.latest IS DISTINCT FROM NEW.latest
- OR OLD.c_released IS DISTINCT FROM NEW.c_released
- OR OLD.c_languages IS DISTINCT FROM NEW.c_languages
+ WHEN ((OLD.rgraph IS NOT NULL AND NEW.rgraph IS NULL)
+ OR (NEW.rgraph IS NOT NULL AND (OLD.c_released IS DISTINCT FROM NEW.c_released OR OLD.c_languages IS DISTINCT FROM NEW.c_languages))
) EXECUTE PROCEDURE vn_relgraph_notify();
-CREATE TRIGGER producer_relgraph_notify AFTER UPDATE ON producers FOR EACH ROW
- WHEN (OLD.rgraph IS DISTINCT FROM NEW.rgraph
- OR OLD.latest IS DISTINCT FROM NEW.latest
- ) EXECUTE PROCEDURE producer_relgraph_notify();
+CREATE TRIGGER producer_relgraph_notify AFTER UPDATE ON producers FOR EACH ROW WHEN (OLD.rgraph IS NOT NULL AND NEW.rgraph IS NULL) EXECUTE PROCEDURE producer_relgraph_notify();
CREATE TRIGGER insert_notify AFTER INSERT ON changes FOR EACH STATEMENT EXECUTE PROCEDURE insert_notify();
CREATE TRIGGER insert_notify AFTER INSERT ON threads_posts FOR EACH STATEMENT EXECUTE PROCEDURE insert_notify();
CREATE TRIGGER insert_notify AFTER INSERT ON tags FOR EACH STATEMENT EXECUTE PROCEDURE insert_notify();
CREATE TRIGGER insert_notify AFTER INSERT ON traits FOR EACH STATEMENT EXECUTE PROCEDURE insert_notify();
-CREATE TRIGGER release_vncache_update AFTER UPDATE ON releases FOR EACH ROW
- WHEN (OLD.latest IS DISTINCT FROM NEW.latest OR OLD.hidden IS DISTINCT FROM NEW.hidden)
- EXECUTE PROCEDURE release_vncache_update();
-
CREATE TRIGGER notify_pm AFTER INSERT ON threads_posts FOR EACH ROW EXECUTE PROCEDURE notify_pm();
-CREATE TRIGGER notify_dbdel AFTER UPDATE ON vn FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
-CREATE TRIGGER notify_dbdel AFTER UPDATE ON producers FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
-CREATE TRIGGER notify_dbdel AFTER UPDATE ON releases FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
-CREATE TRIGGER notify_dbdel AFTER UPDATE ON chars FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
-CREATE TRIGGER notify_dbdel AFTER UPDATE ON staff FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_dbdel();
-CREATE TRIGGER notify_listdel AFTER UPDATE ON vn FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_listdel();
-CREATE TRIGGER notify_listdel AFTER UPDATE ON releases FOR EACH ROW WHEN (NOT OLD.hidden AND NEW.hidden) EXECUTE PROCEDURE notify_listdel();
-CREATE TRIGGER notify_dbedit AFTER UPDATE ON vn FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
-CREATE TRIGGER notify_dbedit AFTER UPDATE ON producers FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
-CREATE TRIGGER notify_dbedit AFTER UPDATE ON releases FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
-CREATE TRIGGER notify_dbedit AFTER UPDATE ON chars FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
-CREATE TRIGGER notify_dbedit AFTER UPDATE ON staff FOR EACH ROW WHEN (OLD.latest IS DISTINCT FROM NEW.latest AND NOT NEW.hidden) EXECUTE PROCEDURE notify_dbedit();
CREATE TRIGGER notify_announce AFTER INSERT ON threads_posts FOR EACH ROW WHEN (NEW.num = 1) EXECUTE PROCEDURE notify_announce();
-CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON vn FOR EACH ROW
- WHEN (OLD.c_search IS NOT NULL AND NEW.c_search IS NULL
- OR NEW.latest IS DISTINCT FROM OLD.latest
- ) EXECUTE PROCEDURE vn_vnsearch_notify();
-CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON releases FOR EACH ROW
- WHEN (NEW.hidden IS DISTINCT FROM OLD.hidden OR NEW.latest IS DISTINCT FROM OLD.latest)
- EXECUTE PROCEDURE vn_vnsearch_notify();
+CREATE TRIGGER vn_vnsearch_notify AFTER UPDATE ON vn FOR EACH ROW WHEN (OLD.c_search IS NOT NULL AND NEW.c_search IS NULL) EXECUTE PROCEDURE vn_vnsearch_notify();
CREATE CONSTRAINT TRIGGER update_vnlist_rlist AFTER DELETE ON vnlists DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE update_vnlist_rlist();
CREATE CONSTRAINT TRIGGER update_vnlist_rlist AFTER INSERT ON rlists DEFERRABLE FOR EACH ROW EXECUTE PROCEDURE update_vnlist_rlist();
diff --git a/util/sqleditfunc.pl b/util/sqleditfunc.pl
new file mode 100755
index 00000000..773214f9
--- /dev/null
+++ b/util/sqleditfunc.pl
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use List::Util 'any';
+
+use Cwd 'abs_path';
+our $ROOT;
+BEGIN { ($ROOT = abs_path $0) =~ s{/util/sqleditfunc\.pl$}{}; }
+
+my %tabletypes; # table_name => dbentry_type
+my %tables; # table_name => [ column_names ]
+my %items; # item_name => { tables_without_hist => [ data_column_names ] }
+
+
+# Fills %tables
+sub readschema {
+ open my $F, '<', "$ROOT/util/sql/schema.sql" or die $!;
+ my $table = '';
+ while(<$F>) {
+ chomp;
+ if(/^\s*CREATE\s+TABLE\s+([^ ]+)/) {
+ $table = $1;
+ $tables{$table} = [];
+ $tabletypes{$table} = $1 if /--.*\s+dbentry_type=(.)/;
+ } elsif($table && /^\s+("?[^\( ]+"?)\s/ && !/^\s+PRIMARY\s+KEY/) {
+ push @{$tables{$table}}, $1;
+ }
+ }
+}
+
+
+sub gensql {
+ my($template, $item) = @_;
+
+ # table_name_without_hist => [ column_names_without_chid ]
+ my %ts = map +($_, [ grep !/^chid$/, @{$tables{"${_}_hist"}} ]), map /^${item}_/ && /^(.+)_hist$/ ? $1 : (), keys %tables;
+
+ my %replace = ( item => $item, itemtype => $tabletypes{$item} );
+ $replace{createtemptables} = join "\n", map sprintf(
+ " CREATE TEMPORARY TABLE edit_%s (LIKE %s INCLUDING DEFAULTS INCLUDING CONSTRAINTS);\n".
+ " ALTER TABLE edit_%1\$s DROP COLUMN %s;",
+ $_, $_ eq 'staff_alias' ? ($_, 'id') : ("${_}_hist", 'chid') # staff_alias copies from the non-_hist table, because it needs the sequence
+ ), sort keys %ts;
+ $replace{temptablenames} = join ', ', map "edit_$_", sort keys %ts;
+ $replace{loadtemptables} = join "\n", map sprintf(
+ " INSERT INTO edit_%s (%s) SELECT %2\$s FROM %1\$s_hist WHERE chid = xchid;",
+ $_, join ', ', @{$ts{$_}}), sort keys %ts;
+ $replace{copyfromtemp} = join "\n", map sprintf(
+ " DELETE FROM %1\$s WHERE id = r.itemid;\n".
+ " INSERT INTO %1\$s (id, %2\$s) SELECT r.itemid, %2\$s FROM edit_%1\$s;\n".
+ " INSERT INTO %1\$s_hist (chid, %2\$s) SELECT r.chid, %2\$s FROM edit_%1\$s;",
+ $_, join ', ', @{$ts{$_}}), grep $_ ne $item, sort keys %ts;
+ $replace{copymainfromtemp} = sprintf
+ " INSERT INTO %1\$s_hist (chid, %2\$s) SELECT r.chid, %2\$s FROM edit_%1\$s;\n".
+ " UPDATE %1\$s SET locked = (SELECT ilock FROM edit_revision), hidden = (SELECT ihid FROM edit_revision),\n".
+ " %3\$s FROM edit_%1\$s x WHERE id = r.itemid;",
+ $item, join(', ', @{$ts{$item}}), join(', ', map "$_ = x.$_", @{$ts{$item}});
+
+ $template =~ s/{([a-z]+)}/$replace{$1}/g;
+ $template;
+}
+
+
+readschema;
+my $template = join '', <DATA>;
+
+open my $F, '>', "$ROOT/util/sql/editfunc.sql" or die $!;
+print $F "-- Automatically generated by util/sqleditfunc.pl. DO NOT EDIT.\n";
+print $F gensql $template, $_ for sort keys %tabletypes;
+
+
+__DATA__
+
+CREATE OR REPLACE FUNCTION edit_{itemtype}_init(xid integer, xrev integer) RETURNS void AS $$
+DECLARE
+ xchid integer;
+BEGIN
+ -- create tables, based on existing tables
+ BEGIN
+{createtemptables}
+ EXCEPTION WHEN duplicate_table THEN
+ TRUNCATE {temptablenames};
+ END;
+ -- Create edit_revision table and get relevant change ID.
+ SELECT edit_revtable('{itemtype}', xid, xrev) INTO xchid;
+ -- new entry, load defaults
+ IF xchid IS NULL THEN
+ INSERT INTO edit_{item} DEFAULT VALUES;
+ -- otherwise, load revision
+ ELSE
+{loadtemptables}
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION edit_{itemtype}_commit() RETURNS edit_rettype AS $$
+DECLARE
+ r edit_rettype;
+BEGIN
+ IF (SELECT COUNT(*) FROM edit_{item}) <> 1 THEN
+ RAISE 'edit_{item} must have exactly one row!';
+ END IF;
+ SELECT INTO r * FROM edit_commit();
+{copyfromtemp}
+{copymainfromtemp}
+ PERFORM edit_committed('{itemtype}', r);
+ RETURN r;
+END;
+$$ LANGUAGE plpgsql;
+
diff --git a/util/updates/update_2.25-sqlsplit.sql b/util/updates/update_2.25-sqlsplit.sql
new file mode 100644
index 00000000..b7ffa91f
--- /dev/null
+++ b/util/updates/update_2.25-sqlsplit.sql
@@ -0,0 +1,258 @@
+-- Q: Why recreate all the tables rather than modify existing ones?
+-- A: Because the production tables have been modified many times, and columns
+-- weren't always in the same order as in scheme.sql. Recreating everything
+-- also has the advantage of ensuring that all references and indices are
+-- handled and documented here. In hindsight, it also seems like the easier
+-- approach.
+
+ALTER TABLE changes RENAME TO changes_old;
+ALTER TABLE chars RENAME TO chars_old;
+ALTER TABLE chars_rev RENAME TO chars_rev_old;
+ALTER TABLE chars_traits RENAME TO chars_traits_old;
+ALTER TABLE chars_vns RENAME TO chars_vns_old;
+ALTER TABLE producers RENAME TO producers_old;
+ALTER TABLE producers_rev RENAME TO producers_rev_old;
+ALTER TABLE producers_relations RENAME TO producers_relations_old;
+ALTER TABLE releases RENAME TO releases_old;
+ALTER TABLE releases_rev RENAME TO releases_rev_old;
+ALTER TABLE releases_lang RENAME TO releases_lang_old;
+ALTER TABLE releases_media RENAME TO releases_media_old;
+ALTER TABLE releases_platforms RENAME TO releases_platforms_old;
+ALTER TABLE releases_producers RENAME TO releases_producers_old;
+ALTER TABLE releases_vn RENAME TO releases_vn_old;
+ALTER TABLE staff RENAME TO staff_old;
+ALTER TABLE staff_rev RENAME TO staff_rev_old;
+ALTER TABLE staff_alias RENAME TO staff_alias_old;
+ALTER TABLE vn RENAME TO vn_old;
+ALTER TABLE vn_rev RENAME TO vn_rev_old;
+ALTER TABLE vn_anime RENAME TO vn_anime_old;
+ALTER TABLE vn_relations RENAME TO vn_relations_old;
+ALTER TABLE vn_screenshots RENAME TO vn_screenshots_old;
+ALTER TABLE vn_seiyuu RENAME TO vn_seiyuu_old;
+ALTER TABLE vn_staff RENAME TO vn_staff_old;
+
+-- XXX: The names of these sequences depend on how the corresponding tables
+-- were generated. The names below are the ones in the production database.
+ALTER SEQUENCE changes_id_seq RENAME TO changes_id_seq_old;
+ALTER SEQUENCE chars_id_seq RENAME TO chars_id_seq_old;
+ALTER SEQUENCE producers_id_seq RENAME TO producers_id_seq_old;
+ALTER SEQUENCE releases_id_seq RENAME TO releases_id_seq_old;
+ALTER SEQUENCE staff_alias_id_seq RENAME TO staff_alias_id_seq_old;
+ALTER SEQUENCE staff_id_seq RENAME TO staff_id_seq_old;
+ALTER SEQUENCE vn_id_seq RENAME TO vn_id_seq_old;
+
+\i util/sql/schema.sql
+
+
+-- XXX: This query uses a window function to generate changes.rev instead of
+-- copying the value from the old table. This is done because, in the old
+-- database schema, there was no uniqueness constraint on (type, itemid, rev),
+-- and due to a race condition it was possible for duplicates to appear. This
+-- is a pretty rare occurence, and easy to correct by renumbering the changes.
+-- (Changes the URL of a few revision pages, but there's no way to avoid that)
+INSERT INTO changes SELECT c.id, c.type, COALESCE(vr.vid, pr.pid, rr.rid, cr.cid, sr.sid),
+ row_number() OVER (PARTITION BY c.type, COALESCE(vr.vid, pr.pid, rr.rid, cr.cid, sr.sid) ORDER BY c.id ASC),
+ c.added, c.requester, c.ip, c.comments, c.ihid, c.ilock
+ FROM changes_old c
+ LEFT JOIN vn_rev_old vr ON vr.id = c.id
+ LEFT JOIN producers_rev_old pr ON pr.id = c.id
+ LEFT JOIN releases_rev_old rr ON rr.id = c.id
+ LEFT JOIN chars_rev_old cr ON cr.id = c.id
+ LEFT JOIN staff_rev_old sr ON sr.id = c.id;
+
+INSERT INTO chars SELECT c.id, c.locked, c.hidden,
+ cr.name, cr.original, cr.alias, cr.image, cr.desc, cr.gender, cr.s_bust, cr.s_waist, cr.s_hip,
+ cr.b_month, cr.b_day, cr.height, cr.weight, cr.bloodt, cr.main, cr.main_spoil
+ FROM chars_old c JOIN chars_rev_old cr ON cr.id = c.latest;
+
+INSERT INTO chars_hist SELECT cr.id,
+ cr.name, cr.original, cr.alias, cr.image, cr.desc, cr.gender, cr.s_bust, cr.s_waist, cr.s_hip,
+ cr.b_month, cr.b_day, cr.height, cr.weight, cr.bloodt, cr.main, cr.main_spoil
+ FROM chars_rev_old cr;
+
+INSERT INTO chars_traits SELECT c.id, ct.tid, ct.spoil
+ FROM chars_old c
+ JOIN chars_traits_old ct ON ct.cid = c.latest;
+
+INSERT INTO chars_traits_hist SELECT cid, tid, spoil
+ FROM chars_traits_old;
+
+INSERT INTO chars_vns SELECT c.id, cv.vid, cv.rid, cv.spoil, cv.role
+ FROM chars_old c
+ JOIN chars_vns_old cv ON cv.cid = c.latest;
+
+INSERT INTO chars_vns_hist SELECT cid, vid, rid, spoil, role
+ FROM chars_vns_old;
+
+INSERT INTO producers SELECT p.id, p.locked, p.hidden,
+ pr.type, pr.name, pr.original, pr.website, pr.lang, pr.desc, pr.alias, pr.l_wp, p.rgraph
+ FROM producers_old p JOIN producers_rev_old pr ON pr.id = p.latest;
+
+INSERT INTO producers_hist SELECT id, type, name, original, website, lang, "desc", alias, l_wp
+ FROM producers_rev_old;
+
+INSERT INTO producers_relations SELECT p.id, pr.pid2, pr.relation
+ FROM producers_old p
+ JOIN producers_relations_old pr ON p.latest = pr.pid1;
+
+INSERT INTO producers_relations_hist SELECT pid1, pid2, relation
+ FROM producers_relations_old;
+
+INSERT INTO releases SELECT r.id, r.locked, r.hidden,
+ rr.title, rr.original, rr.type, rr.website, rr.catalog, rr.gtin, rr.released, rr.notes, rr.minage, rr.patch,
+ rr.freeware, rr.doujin, rr.resolution, rr.voiced, rr.ani_story, rr.ani_ero
+ FROM releases_old r JOIN releases_rev_old rr ON rr.id = r.latest;
+
+INSERT INTO releases_hist SELECT rr.id,
+ rr.title, rr.original, rr.type, rr.website, rr.catalog, rr.gtin, rr.released, rr.notes, rr.minage, rr.patch,
+ rr.freeware, rr.doujin, rr.resolution, rr.voiced, rr.ani_story, rr.ani_ero
+ FROM releases_rev_old rr;
+
+INSERT INTO releases_lang SELECT r.id, rl.lang
+ FROM releases_old r JOIN releases_lang_old rl ON rl.rid = r.latest;
+
+INSERT INTO releases_lang_hist SELECT rl.rid, rl.lang
+ FROM releases_lang_old rl;
+
+INSERT INTO releases_media SELECT r.id, rm.medium, rm.qty
+ FROM releases_old r JOIN releases_media_old rm ON rm.rid = r.latest;
+
+INSERT INTO releases_media_hist SELECT rm.rid, rm.medium, rm.qty
+ FROM releases_media_old rm;
+
+INSERT INTO releases_platforms SELECT r.id, rp.platform
+ FROM releases_old r JOIN releases_platforms_old rp ON rp.rid = r.latest;
+
+INSERT INTO releases_platforms_hist SELECT rp.rid, rp.platform
+ FROM releases_platforms_old rp;
+
+INSERT INTO releases_producers SELECT r.id, rp.pid, rp.developer, rp.publisher
+ FROM releases_old r JOIN releases_producers_old rp ON rp.rid = r.latest;
+
+INSERT INTO releases_producers_hist SELECT rp.rid, rp.pid, rp.developer, rp.publisher
+ FROM releases_producers_old rp;
+
+INSERT INTO releases_vn SELECT r.id, rv.vid
+ FROM releases_old r JOIN releases_vn_old rv ON rv.rid = r.latest;
+
+INSERT INTO releases_vn_hist SELECT rv.rid, rv.vid
+ FROM releases_vn_old rv;
+
+INSERT INTO staff SELECT s.id, s.locked, s.hidden,
+ sr.aid, sr.gender, sr.lang, sr.desc, sr.l_wp, sr.l_site, sr.l_twitter, sr.l_anidb
+ FROM staff_old s JOIN staff_rev_old sr ON sr.id = s.latest;
+
+INSERT INTO staff_hist SELECT sr.id,
+ sr.aid, sr.gender, sr.lang, sr.desc, sr.l_wp, sr.l_site, sr.l_twitter, sr.l_anidb
+ FROM staff_rev_old sr;
+
+INSERT INTO staff_alias SELECT s.id, sa.id, sa.name, sa.original
+ FROM staff_old s JOIN staff_alias_old sa ON sa.rid = s.latest;
+
+INSERT INTO staff_alias_hist SELECT rid, id, name, original
+ FROM staff_alias_old;
+
+INSERT INTO vn SELECT v.id, v.locked, v.hidden,
+ vr.title, vr.original, vr.alias, vr.length, vr.img_nsfw, vr.image, vr.desc, vr.l_wp, vr.l_encubed, vr.l_renai,
+ v.rgraph, v.c_released, v.c_languages, v.c_olang, v.c_platforms, v.c_popularity, v.c_rating, v.c_votecount, v.c_search
+ FROM vn_old v JOIN vn_rev_old vr ON vr.id = v.latest;
+
+INSERT INTO vn_hist SELECT vr.id,
+ vr.title, vr.original, vr.alias, vr.length, vr.img_nsfw, vr.image, vr.desc, vr.l_wp, vr.l_encubed, vr.l_renai
+ FROM vn_rev_old vr;
+
+INSERT INTO vn_anime SELECT v.id, va.aid
+ FROM vn_old v JOIN vn_anime_old va ON va.vid = v.latest;
+
+INSERT INTO vn_anime_hist SELECT vid, aid
+ FROM vn_anime_old;
+
+INSERT INTO vn_relations SELECT v.id, vr.vid2, vr.relation, vr.official
+ FROM vn_old v JOIN vn_relations_old vr ON vr.vid1 = v.latest;
+
+INSERT INTO vn_relations_hist SELECT vid1, vid2, relation, official
+ FROM vn_relations_old;
+
+INSERT INTO vn_screenshots SELECT v.id, vs.scr, vs.rid, vs.nsfw
+ FROM vn_old v JOIN vn_screenshots_old vs ON vs.vid = v.latest;
+
+INSERT INTO vn_screenshots_hist SELECT vid, scr, rid, nsfw
+ FROM vn_screenshots_old;
+
+INSERT INTO vn_seiyuu SELECT v.id, vs.aid, vs.cid, vs.note
+ FROM vn_old v JOIN vn_seiyuu_old vs ON vs.vid = v.latest;
+
+INSERT INTO vn_seiyuu_hist SELECT vid, aid, cid, note
+ FROM vn_seiyuu_old;
+
+INSERT INTO vn_staff SELECT v.id, vs.aid, vs.role, vs.note
+ FROM vn_old v JOIN vn_staff_old vs ON vs.vid = v.latest;
+
+INSERT INTO vn_staff_hist SELECT vid, aid, role, note
+ FROM vn_staff_old;
+
+
+SELECT setval('changes_id_seq', nextval('changes_id_seq_old'));
+SELECT setval('chars_id_seq', nextval('chars_id_seq_old'));
+SELECT setval('producers_id_seq', nextval('producers_id_seq_old'));
+SELECT setval('releases_id_seq', nextval('releases_id_seq_old'));
+SELECT setval('staff_alias_aid_seq', nextval('staff_alias_id_seq_old')); -- note the change from id to aid
+SELECT setval('staff_id_seq', nextval('staff_id_seq_old'));
+SELECT setval('vn_id_seq', nextval('vn_id_seq_old'));
+
+
+-- Dropping all tables with CASCADE causes all foreign key references to and
+-- from the tables to be dropped as well. This is exactly what we want, so we
+-- can re-add the constraints on the newly created tables.
+DROP TABLE changes_old CASCADE;
+DROP TABLE chars_old CASCADE;
+DROP TABLE chars_rev_old CASCADE;
+DROP TABLE chars_traits_old CASCADE;
+DROP TABLE chars_vns_old CASCADE;
+DROP TABLE producers_old CASCADE;
+DROP TABLE producers_rev_old CASCADE;
+DROP TABLE producers_relations_old CASCADE;
+DROP TABLE releases_old CASCADE;
+DROP TABLE releases_rev_old CASCADE;
+DROP TABLE releases_lang_old CASCADE;
+DROP TABLE releases_media_old CASCADE;
+DROP TABLE releases_platforms_old CASCADE;
+DROP TABLE releases_producers_old CASCADE;
+DROP TABLE releases_vn_old CASCADE;
+DROP TABLE staff_old CASCADE;
+DROP TABLE staff_rev_old CASCADE;
+DROP TABLE staff_alias_old CASCADE;
+DROP TABLE vn_old CASCADE;
+DROP TABLE vn_rev_old CASCADE;
+DROP TABLE vn_anime_old CASCADE;
+DROP TABLE vn_relations_old CASCADE;
+DROP TABLE vn_screenshots_old CASCADE;
+DROP TABLE vn_seiyuu_old CASCADE;
+DROP TABLE vn_staff_old CASCADE;
+
+DROP INDEX threads_posts_ts;
+
+DROP FUNCTION edit_revtable(dbentry_type, integer);
+DROP FUNCTION edit_vn_init(integer);
+DROP FUNCTION edit_vn_commit();
+DROP FUNCTION edit_release_init(integer);
+DROP FUNCTION edit_release_commit();
+DROP FUNCTION edit_producer_init(integer);
+DROP FUNCTION edit_producer_commit();
+DROP FUNCTION edit_char_init(integer);
+DROP FUNCTION edit_char_commit();
+DROP FUNCTION edit_staff_init(integer);
+DROP FUNCTION edit_staff_commit();
+DROP FUNCTION release_vncache_update();
+DROP FUNCTION notify_dbdel();
+DROP FUNCTION notify_dbedit();
+DROP FUNCTION notify_listdel();
+DROP FUNCTION update_hidlock();
+
+DROP TYPE edit_rettype CASCADE;
+CREATE TYPE edit_rettype AS (itemid integer, chid integer, rev integer);
+
+\i util/sql/func.sql
+\i util/sql/editfunc.sql
+\i util/sql/tableattrs.sql
+\i util/sql/triggers.sql
diff --git a/util/updates/update_2.26.sql b/util/updates/update_2.26.sql
new file mode 100644
index 00000000..6900d080
--- /dev/null
+++ b/util/updates/update_2.26.sql
@@ -0,0 +1,9 @@
+-- No more 'staffedit' permission flag
+UPDATE users SET perm = (perm & ~8);
+
+-- Removed support for sha256-hashed passwords
+UPDATE users SET passwd = '' WHERE length(passwd) = 41;
+
+-- Need to regenerate all relation graphs in the switch to HTML5
+UPDATE vn SET rgraph = NULL;
+UPDATE producers SET rgraph = NULL;