Skip to content

Commit bf2b20d

Browse files
committed
diskquota/jdbc: fix renameLayer leaving TILESET.KEY stale (#1526)
JDBCQuotaStore.renameLayer only updated LAYER_NAME, leaving the layer-name prefix in TILESET.KEY and TILEPAGE.TILESET_ID stale - later lookups missed the row, getOrCreateTileSet inserted duplicates, and quota by id was split across two rows. The rename SQL now rewrites KEY too; ON UPDATE CASCADE on the TILEPAGE FK keeps TILESET_ID in lockstep. OracleDialect keeps the legacy behavior (no ON UPDATE CASCADE on Oracle); a follow-up will use deferrable constraints there. on-behalf-of: @camptocamp <info@camptocamp.com>
1 parent 94d8a0f commit bf2b20d

7 files changed

Lines changed: 673 additions & 196 deletions

File tree

geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/HSQLDialect.java

Lines changed: 34 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414
package org.geowebcache.diskquota.jdbc;
1515

16-
import java.util.Arrays;
16+
import java.util.List;
1717

1818
/**
1919
* HSQL dialect for the quota store
@@ -25,69 +25,44 @@ public HSQLDialect() {
2525

2626
TABLE_CREATION_MAP.put(
2727
"TILESET",
28-
Arrays.asList( //
29-
"CREATE CACHED TABLE ${schema}TILESET (\n"
30-
+ //
31-
" KEY VARCHAR("
32-
+ TILESET_KEY_SIZE
33-
+ ") PRIMARY KEY,\n"
34-
+ //
35-
" LAYER_NAME VARCHAR("
36-
+ LAYER_NAME_SIZE
37-
+ "),\n"
38-
+ //
39-
" GRIDSET_ID VARCHAR("
40-
+ GRIDSET_ID_SIZE
41-
+ "),\n"
42-
+ //
43-
" BLOB_FORMAT VARCHAR("
44-
+ BLOB_FORMAT_SIZE
45-
+ "),\n"
46-
+ //
47-
" PARAMETERS_ID VARCHAR("
48-
+ PARAMETERS_ID_SIZE
49-
+ "),\n"
50-
+ //
51-
" BYTES NUMERIC("
52-
+ BYTES_SIZE
53-
+ ") DEFAULT 0 NOT NULL\n"
54-
+ //
55-
")", //
28+
List.of( //
29+
"""
30+
CREATE CACHED TABLE ${schema}TILESET (
31+
KEY VARCHAR(%d) PRIMARY KEY,
32+
LAYER_NAME VARCHAR(%d),
33+
GRIDSET_ID VARCHAR(%d),
34+
BLOB_FORMAT VARCHAR(%d),
35+
PARAMETERS_ID VARCHAR(%d),
36+
BYTES NUMERIC(%d) DEFAULT 0 NOT NULL
37+
)
38+
"""
39+
.formatted(
40+
TILESET_KEY_SIZE,
41+
LAYER_NAME_SIZE,
42+
GRIDSET_ID_SIZE,
43+
BLOB_FORMAT_SIZE,
44+
PARAMETERS_ID_SIZE,
45+
BYTES_SIZE), //
5646
"CREATE INDEX TILESET_LAYER ON ${schema}TILESET(LAYER_NAME)" //
5747
));
5848

5949
TABLE_CREATION_MAP.put(
6050
"TILEPAGE",
61-
Arrays.asList(
62-
"CREATE CACHED TABLE ${schema}TILEPAGE (\n"
63-
+ //
64-
" KEY VARCHAR("
65-
+ TILEPAGE_KEY_SIZE
66-
+ ") PRIMARY KEY,\n"
67-
+ //
68-
" TILESET_ID VARCHAR("
69-
+ TILESET_KEY_SIZE
70-
+ ") REFERENCES ${schema}TILESET(KEY) ON DELETE CASCADE,\n"
71-
+ //
72-
" PAGE_Z SMALLINT,\n"
73-
+ //
74-
" PAGE_X INTEGER,\n"
75-
+ //
76-
" PAGE_Y INTEGER,\n"
77-
+ //
78-
" CREATION_TIME_MINUTES INTEGER,\n"
79-
+ //
80-
" FREQUENCY_OF_USE FLOAT,\n"
81-
+ //
82-
" LAST_ACCESS_TIME_MINUTES INTEGER,\n"
83-
+ //
84-
" FILL_FACTOR FLOAT,\n"
85-
+ //
86-
" NUM_HITS NUMERIC("
87-
+ NUM_HITS_SIZE
88-
+ ")\n"
89-
+ //
90-
")", //
51+
List.of(
52+
"""
53+
CREATE CACHED TABLE ${schema}TILEPAGE (
54+
KEY VARCHAR(%d) PRIMARY KEY,
55+
TILESET_ID VARCHAR(%d) REFERENCES ${schema}TILESET(KEY) ON UPDATE CASCADE ON DELETE CASCADE,
56+
PAGE_Z SMALLINT,
57+
PAGE_X INTEGER,
58+
PAGE_Y INTEGER,
59+
CREATION_TIME_MINUTES INTEGER,
60+
FREQUENCY_OF_USE FLOAT,
61+
LAST_ACCESS_TIME_MINUTES INTEGER,
62+
FILL_FACTOR FLOAT,
63+
NUM_HITS NUMERIC(%d)
64+
)"""
65+
.formatted(TILEPAGE_KEY_SIZE, TILESET_KEY_SIZE, NUM_HITS_SIZE), //
9166
"CREATE INDEX TILEPAGE_TILESET ON ${schema}TILEPAGE(TILESET_ID, FILL_FACTOR)",
9267
"CREATE INDEX TILEPAGE_FREQUENCY ON ${schema}TILEPAGE(FREQUENCY_OF_USE DESC)",
9368
"CREATE INDEX TILEPAGE_LAST_ACCESS ON ${schema}TILEPAGE(LAST_ACCESS_TIME_MINUTES DESC)"));

geowebcache/diskquota/jdbc/src/main/java/org/geowebcache/diskquota/jdbc/OracleDialect.java

Lines changed: 68 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*/
1414
package org.geowebcache.diskquota.jdbc;
1515

16-
import java.util.Arrays;
1716
import java.util.List;
1817

1918
/**
@@ -34,69 +33,45 @@ static int numberPrecision(int n) {
3433
public OracleDialect() {
3534
TABLE_CREATION_MAP.put(
3635
"TILESET",
37-
Arrays.asList( //
38-
"CREATE TABLE ${schema}TILESET (\n"
39-
+ //
40-
" KEY VARCHAR("
41-
+ TILESET_KEY_SIZE
42-
+ ") PRIMARY KEY,\n"
43-
+ //
44-
" LAYER_NAME VARCHAR("
45-
+ LAYER_NAME_SIZE
46-
+ "),\n"
47-
+ //
48-
" GRIDSET_ID VARCHAR("
49-
+ GRIDSET_ID_SIZE
50-
+ "),\n"
51-
+ //
52-
" BLOB_FORMAT VARCHAR("
53-
+ BLOB_FORMAT_SIZE
54-
+ "),\n"
55-
+ //
56-
" PARAMETERS_ID VARCHAR("
57-
+ PARAMETERS_ID_SIZE
58-
+ "),\n"
59-
+ //
60-
" BYTES NUMBER("
61-
+ numberPrecision(BYTES_SIZE)
62-
+ ") DEFAULT 0 NOT NULL\n"
63-
+ //
64-
") ORGANIZATION INDEX", //
36+
List.of( //
37+
"""
38+
CREATE TABLE ${schema}TILESET (
39+
KEY VARCHAR(%d) PRIMARY KEY,
40+
LAYER_NAME VARCHAR(%d),
41+
GRIDSET_ID VARCHAR(%d),
42+
BLOB_FORMAT VARCHAR(%d),
43+
PARAMETERS_ID VARCHAR(%d),
44+
BYTES NUMBER(%d) DEFAULT 0 NOT NULL
45+
) ORGANIZATION INDEX
46+
"""
47+
.formatted(
48+
TILESET_KEY_SIZE,
49+
LAYER_NAME_SIZE,
50+
GRIDSET_ID_SIZE,
51+
BLOB_FORMAT_SIZE,
52+
PARAMETERS_ID_SIZE,
53+
numberPrecision(BYTES_SIZE)), //
6554
"CREATE INDEX TILESET_LAYER ON TILESET(LAYER_NAME)" //
6655
));
6756

6857
TABLE_CREATION_MAP.put(
6958
"TILEPAGE",
70-
Arrays.asList(
71-
"CREATE TABLE ${schema}TILEPAGE (\n"
72-
+ //
73-
" KEY VARCHAR("
74-
+ TILEPAGE_KEY_SIZE
75-
+ ") PRIMARY KEY,\n"
76-
+ //
77-
" TILESET_ID VARCHAR("
78-
+ TILESET_KEY_SIZE
79-
+ ") REFERENCES ${schema}TILESET(KEY) ON DELETE CASCADE,\n"
80-
+ //
81-
" PAGE_Z SMALLINT,\n"
82-
+ //
83-
" PAGE_X INTEGER,\n"
84-
+ //
85-
" PAGE_Y INTEGER,\n"
86-
+ //
87-
" CREATION_TIME_MINUTES INTEGER,\n"
88-
+ //
89-
" FREQUENCY_OF_USE FLOAT,\n"
90-
+ //
91-
" LAST_ACCESS_TIME_MINUTES INTEGER,\n"
92-
+ //
93-
" FILL_FACTOR FLOAT,\n"
94-
+ //
95-
" NUM_HITS NUMBER("
96-
+ numberPrecision(NUM_HITS_SIZE)
97-
+ ")\n"
98-
+ //
99-
") ORGANIZATION INDEX", //
59+
List.of(
60+
"""
61+
CREATE TABLE ${schema}TILEPAGE (
62+
KEY VARCHAR(%d) PRIMARY KEY,
63+
TILESET_ID VARCHAR(%d) REFERENCES ${schema}TILESET(KEY) ON DELETE CASCADE,
64+
PAGE_Z SMALLINT,
65+
PAGE_X INTEGER,
66+
PAGE_Y INTEGER,
67+
CREATION_TIME_MINUTES INTEGER,
68+
FREQUENCY_OF_USE FLOAT,
69+
LAST_ACCESS_TIME_MINUTES INTEGER,
70+
FILL_FACTOR FLOAT,
71+
NUM_HITS NUMBER(%d)
72+
) ORGANIZATION INDEX
73+
"""
74+
.formatted(TILEPAGE_KEY_SIZE, TILESET_KEY_SIZE, numberPrecision(NUM_HITS_SIZE)), //
10075
"CREATE INDEX TILEPAGE_TILESET ON TILEPAGE(TILESET_ID)",
10176
"CREATE INDEX TILEPAGE_FILL_FACTOR ON TILEPAGE(FILL_FACTOR)",
10277
"CREATE INDEX TILEPAGE_FREQUENCY ON TILEPAGE(FREQUENCY_OF_USE DESC)",
@@ -108,6 +83,40 @@ protected void addEmtpyTableReference(StringBuilder sb) {
10883
sb.append("FROM DUAL");
10984
}
11085

86+
/**
87+
* No-op: Oracle does not support {@code ON UPDATE CASCADE} on foreign keys, so there is nothing portable to
88+
* migrate. Companion to {@link #getRenameLayerStatement(String, String, String)}, which preserves the legacy
89+
* LAYER_NAME-only behavior on this dialect.
90+
*/
91+
@Override
92+
public void migrateForeignKeys(String schema, SimpleJdbcTemplate template) {
93+
// intentional no-op
94+
}
95+
96+
/**
97+
* Oracle does not support {@code ON UPDATE CASCADE} on foreign keys, so the {@code TILEPAGE.TILESET_ID -> TILESET
98+
* .KEY} FK declared above only cascades on delete. As a result this dialect cannot safely rewrite {@code TILESET
99+
* .KEY} during a rename without first dealing with the dangling {@code TILEPAGE} rows.
100+
*
101+
* <p>For now Oracle keeps the legacy behavior of only updating {@code LAYER_NAME}; lookups by id against the
102+
* renamed layer will continue to miss the row and cause {@code getOrCreateTileSet} to insert duplicates. Fixing
103+
* this on Oracle (e.g. via {@code DEFERRABLE INITIALLY DEFERRED} constraints, or by disabling the FK around the
104+
* rename) is tracked separately.
105+
*/
106+
@Override
107+
public String getRenameLayerStatement(String schema, String oldLayerName, String newLayerName) {
108+
StringBuilder sb = new StringBuilder("UPDATE ");
109+
if (schema != null) {
110+
sb.append(schema).append(".");
111+
}
112+
sb.append("TILESET SET LAYER_NAME = :")
113+
.append(newLayerName)
114+
.append(" WHERE LAYER_NAME = :")
115+
.append(oldLayerName);
116+
117+
return sb.toString();
118+
}
119+
111120
@Override
112121
public String getLeastFrequentlyUsedPage(String schema, List<String> layerParamNames) {
113122
StringBuilder sb = new StringBuilder("SELECT * FROM (");

0 commit comments

Comments
 (0)