Allow the ota extension to write to tables with no PRIMARY KEY declaration.
FossilOrigin-Name: ba59a7e2ba97244492cbca9247456df0f3f19248
This commit is contained in:
parent
ca3d648f9c
commit
7ca1d07aff
@ -177,6 +177,9 @@ foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_uri} {
|
||||
# relaxed so that external PRIMARY KEYs on tables with automatic rowids
|
||||
# are now allowed.
|
||||
#
|
||||
# UPDATE 2: Tables without any PRIMARY KEY declaration are now allowed.
|
||||
# However the input table must feature an "ota_rowid" column.
|
||||
#
|
||||
reset_db
|
||||
create_ota1 ota.db
|
||||
do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) }
|
||||
@ -186,7 +189,7 @@ do_test 2.2 {
|
||||
} {SQLITE_ERROR}
|
||||
do_test 2.3 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}}
|
||||
} {1 {SQLITE_ERROR - table data_t1 requires ota_rowid column}}
|
||||
reset_db
|
||||
do_execsql_test 2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
|
||||
do_test 2.5 {
|
||||
|
@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test OTA with virtual tables
|
||||
# Test OTA with virtual tables. And tables with no PRIMARY KEY declarations.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
@ -60,6 +60,68 @@ do_execsql_test 1.2.2 { SELECT rowid, * FROM f1 } {
|
||||
do_execsql_test 1.2.3 { INSERT INTO f1(f1) VALUES('integrity-check') }
|
||||
integrity_check 1.2.4
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tables with no PK declaration.
|
||||
#
|
||||
|
||||
# Run the OTA in file $ota on target database $target until completion.
|
||||
#
|
||||
proc run_ota {target ota} {
|
||||
sqlite3ota ota $target $ota
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
1 { }
|
||||
2 {
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
}
|
||||
3 {
|
||||
CREATE INDEX i1 ON t1(b, c);
|
||||
CREATE INDEX i2 ON t1(c, b);
|
||||
CREATE INDEX i3 ON t1(a, a, a, b, b, b, c, c, c);
|
||||
}
|
||||
} {
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.$tn.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1(rowid, a, b, c) VALUES(-1, 'a', 'b', 'c');
|
||||
INSERT INTO t1(rowid, a, b, c) VALUES(-2, 'd', 'e', 'f');
|
||||
}
|
||||
|
||||
db eval $idx
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_t1(ota_rowid, a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(3, 'x', 'y', 'z', 0);
|
||||
INSERT INTO data_t1 VALUES(NULL, 'X', 'Y', 'Z', 0);
|
||||
INSERT INTO data_t1 VALUES('1', NULL, NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(-2, NULL, NULL, 'fff', '..x');
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
run_ota test.db ota.db
|
||||
|
||||
do_execsql_test 2.$tn.3 {
|
||||
SELECT rowid, a, b, c FROM t1 ORDER BY rowid;
|
||||
} {
|
||||
-2 d e fff
|
||||
-1 a b c
|
||||
2 4 5 6
|
||||
3 x y z
|
||||
4 X Y Z
|
||||
}
|
||||
|
||||
integrity_check 2.$tn.4
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -104,8 +104,11 @@ struct OtaObjIter {
|
||||
int nTblCol; /* Size of azTblCol[] array */
|
||||
char **azTblCol; /* Array of quoted column names */
|
||||
unsigned char *abTblPk; /* Array of flags - true for PK columns */
|
||||
int eType;
|
||||
#if 0
|
||||
unsigned char bRowid; /* True for implicit IPK tables */
|
||||
unsigned char bVtab; /* True for a virtual table */
|
||||
#endif
|
||||
|
||||
/* Output variables. zTbl==0 implies EOF. */
|
||||
int bCleanup; /* True in "cleanup" state */
|
||||
@ -124,6 +127,14 @@ struct OtaObjIter {
|
||||
sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
|
||||
};
|
||||
|
||||
/*
|
||||
** Values for OtaObjIter.eType
|
||||
*/
|
||||
#define OTA_PK_REAL 1 /* Table has a real primary key */
|
||||
#define OTA_PK_EXTERNAL 2 /* Table has an external primary key index */
|
||||
#define OTA_PK_NONE 3 /* Table has no PK (use rowid) */
|
||||
#define OTA_PK_VTAB 4 /* Table is a virtual table (use rowid) */
|
||||
|
||||
/*
|
||||
** OTA handle.
|
||||
*/
|
||||
@ -228,8 +239,7 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
|
||||
pIter->nTblCol = 0;
|
||||
sqlite3_free(pIter->zMask);
|
||||
pIter->zMask = 0;
|
||||
pIter->bRowid = 0;
|
||||
pIter->bVtab = 0;
|
||||
pIter->eType = 0; /* Invalid value */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -471,11 +481,11 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
|
||||
if( pIter->azTblCol==0 ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int nCol = 0;
|
||||
int bSeenPk = 0;
|
||||
int i; /* for() loop iterator variable */
|
||||
int rc2; /* sqlite3_finalize() return value */
|
||||
int bOtaRowid = 0; /* If input table has column "ota_rowid" */
|
||||
|
||||
assert( pIter->bRowid==0 && pIter->bVtab==0 );
|
||||
assert( pIter->eType==0 );
|
||||
|
||||
/* Populate the azTblCol[] and nTblCol variables based on the columns
|
||||
** of the input table. Ignore any input table columns that begin with
|
||||
@ -494,6 +504,9 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
|
||||
pIter->azTblCol[pIter->nTblCol++] = zCopy;
|
||||
if( zCopy==0 ) p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
else if( 0==sqlite3_stricmp("ota_rowid", zName) ){
|
||||
bOtaRowid = 1;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = 0;
|
||||
@ -519,20 +532,33 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
|
||||
}else{
|
||||
int iPk = sqlite3_column_int(pStmt, 5);
|
||||
pIter->abTblPk[i] = (iPk!=0);
|
||||
if( iPk ) bSeenPk = 1;
|
||||
if( iPk<0 ) pIter->bRowid = 1;
|
||||
if( iPk ){
|
||||
pIter->eType = (iPk<0) ? OTA_PK_EXTERNAL : OTA_PK_REAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( p->rc==SQLITE_OK ) p->rc = rc2;
|
||||
|
||||
if( p->rc==SQLITE_OK && bSeenPk==0 ){
|
||||
const char *zTab = pIter->zTbl;
|
||||
if( otaIsVtab(p, zTab) ){
|
||||
pIter->bVtab = 1;
|
||||
}else{
|
||||
p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", zTab);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pIter->eType==0 ){
|
||||
/* This must either be a virtual table, or a regular table with no
|
||||
** PRIMARY KEY declaration whatsoever. */
|
||||
if( bOtaRowid==0 ){
|
||||
p->rc = SQLITE_ERROR;
|
||||
p->zErrmsg = sqlite3_mprintf(
|
||||
"table data_%q requires ota_rowid column", pIter->zTbl
|
||||
);
|
||||
}else if( otaIsVtab(p, pIter->zTbl) ){
|
||||
pIter->eType = OTA_PK_VTAB;
|
||||
}else{
|
||||
pIter->eType = OTA_PK_NONE;
|
||||
}
|
||||
}else if( bOtaRowid ){
|
||||
p->rc = SQLITE_ERROR;
|
||||
p->zErrmsg = sqlite3_mprintf(
|
||||
"table data_%q may not have ota_rowid column", pIter->zTbl
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,6 +644,17 @@ static char *otaObjIterGetCollist(
|
||||
return zList;
|
||||
}
|
||||
|
||||
/*
|
||||
** Assuming the current table columns are "a", "b" and "c", and the zObj
|
||||
** paramter is passed "old", return a string of the form:
|
||||
**
|
||||
** "old.a, old.b, old.b"
|
||||
**
|
||||
** With the column names escaped.
|
||||
**
|
||||
** For tables with implicit rowids - OTA_PK_EXTERNAL and OTA_PK_NONE, append
|
||||
** the text ", old._rowid_" to the returned value.
|
||||
*/
|
||||
static char *otaObjIterGetOldlist(
|
||||
sqlite3ota *p,
|
||||
OtaObjIter *pIter,
|
||||
@ -637,21 +674,31 @@ static char *otaObjIterGetOldlist(
|
||||
}
|
||||
|
||||
/* For a table with implicit rowids, append "old._rowid_" to the list. */
|
||||
if( pIter->bRowid ){
|
||||
if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
|
||||
zList = sqlite3_mprintf("%z, %s._rowid_", zList, zObj);
|
||||
}
|
||||
}
|
||||
return zList;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an expression that can be used in a WHERE clause to match the
|
||||
** primary key of the current table. For example, if the table is:
|
||||
**
|
||||
** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c));
|
||||
**
|
||||
** Return the string:
|
||||
**
|
||||
** "b = ?1 AND c = ?2"
|
||||
*/
|
||||
static char *otaObjIterGetWhere(
|
||||
sqlite3ota *p,
|
||||
OtaObjIter *pIter
|
||||
){
|
||||
char *zList = 0;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pIter->bVtab ){
|
||||
zList = otaMPrintfAndCollectError(p, "rowid = ?%d", pIter->nTblCol+1);
|
||||
if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
|
||||
zList = otaMPrintfAndCollectError(p, "_rowid_ = ?%d", pIter->nTblCol+1);
|
||||
}else{
|
||||
const char *zSep = "";
|
||||
int i;
|
||||
@ -759,7 +806,7 @@ static int otaObjIterPrepareAll(
|
||||
int *aiCol; /* Column map */
|
||||
const char **azColl; /* Collation sequences */
|
||||
|
||||
assert( pIter->bVtab==0 );
|
||||
assert( pIter->eType!=OTA_PK_VTAB );
|
||||
|
||||
/* Create the index writers */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
@ -777,10 +824,10 @@ static int otaObjIterPrepareAll(
|
||||
zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
char *zSql;
|
||||
if( pIter->bRowid ){
|
||||
if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT %s, ota_control FROM ota.'ota_tmp_%q' ORDER BY %s%s",
|
||||
zCollist, pIter->zTbl,
|
||||
zCollist, pIter->zTbl,
|
||||
zCollist, zLimit
|
||||
);
|
||||
}else{
|
||||
@ -798,11 +845,13 @@ static int otaObjIterPrepareAll(
|
||||
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz, zSql);
|
||||
}
|
||||
}else{
|
||||
int bOtaRowid = (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE);
|
||||
const char *zTbl = pIter->zTbl;
|
||||
char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol+pIter->bVtab);
|
||||
char *zWhere = otaObjIterGetWhere(p, pIter);
|
||||
char *zOldlist = otaObjIterGetOldlist(p, pIter, "old");
|
||||
char *zNewlist = otaObjIterGetOldlist(p, pIter, "new");
|
||||
char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol + bOtaRowid);
|
||||
|
||||
zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0, 0);
|
||||
pIter->nCol = pIter->nTblCol;
|
||||
|
||||
@ -811,7 +860,7 @@ static int otaObjIterPrepareAll(
|
||||
p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
|
||||
sqlite3_mprintf(
|
||||
"SELECT %s, ota_control%s FROM ota.'data_%q'%s",
|
||||
zCollist, (pIter->bVtab ? ", ota_rowid" : ""), zTbl, zLimit
|
||||
zCollist, (bOtaRowid ? ", ota_rowid" : ""), zTbl, zLimit
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -821,7 +870,7 @@ static int otaObjIterPrepareAll(
|
||||
p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, pz,
|
||||
sqlite3_mprintf(
|
||||
"INSERT INTO main.%Q(%s%s) VALUES(%s)",
|
||||
zTbl, zCollist, (pIter->bVtab ? ", rowid" : ""), zBindings
|
||||
zTbl, zCollist, (bOtaRowid ? ", _rowid_" : ""), zBindings
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -835,8 +884,11 @@ static int otaObjIterPrepareAll(
|
||||
);
|
||||
}
|
||||
|
||||
if( pIter->bVtab==0 ){
|
||||
const char *zOtaRowid = (pIter->bRowid ? ", ota_rowid" : "");
|
||||
if( pIter->eType!=OTA_PK_VTAB ){
|
||||
const char *zOtaRowid = "";
|
||||
if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
|
||||
zOtaRowid = ", ota_rowid";
|
||||
}
|
||||
|
||||
/* Create the ota_tmp_xxx table and the triggers to populate it. */
|
||||
otaMPrintfExec(p,
|
||||
@ -858,12 +910,13 @@ static int otaObjIterPrepareAll(
|
||||
"BEGIN "
|
||||
" INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(3, %s);"
|
||||
"END;"
|
||||
, zTbl, (pIter->bRowid ? ", 0 AS ota_rowid" : ""), zTbl,
|
||||
, zTbl, (pIter->eType==OTA_PK_EXTERNAL ? ", 0 AS ota_rowid" : "")
|
||||
, zTbl,
|
||||
zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
|
||||
zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
|
||||
zTbl, zTbl, zTbl, zCollist, zOtaRowid, zNewlist
|
||||
);
|
||||
if( pIter->bRowid ){
|
||||
if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
|
||||
otaMPrintfExec(p,
|
||||
"CREATE TEMP TRIGGER ota_insert_%q AFTER INSERT ON main.%Q "
|
||||
"BEGIN "
|
||||
@ -1074,6 +1127,15 @@ static int otaStepType(sqlite3ota *p, const char **pzMask){
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
|
||||
const char *zCol = sqlite3_column_name(pStmt, iCol);
|
||||
assert( 0==sqlite3_stricmp(zName, zCol) );
|
||||
}
|
||||
#else
|
||||
# define assertColumnName(x,y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function does the work for an sqlite3ota_step() call.
|
||||
**
|
||||
@ -1122,13 +1184,17 @@ static int otaStep(sqlite3ota *p){
|
||||
pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||
sqlite3_bind_value(pWriter, i+1, pVal);
|
||||
}
|
||||
if( pIter->bVtab ){
|
||||
/* For a virtual table, the SELECT statement is:
|
||||
if( pIter->zIdx==0
|
||||
&& (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE)
|
||||
){
|
||||
/* For a virtual table, or a table with no primary key, the
|
||||
** SELECT statement is:
|
||||
**
|
||||
** SELECT <cols>, ota_control, ota_rowid FROM ....
|
||||
**
|
||||
** Hence column_value(pIter->nCol+1).
|
||||
*/
|
||||
assertColumnName(pIter->pSelect, pIter->nCol+1, "ota_rowid");
|
||||
pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
||||
sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
|
||||
}
|
||||
@ -1143,7 +1209,9 @@ static int otaStep(sqlite3ota *p){
|
||||
pVal = sqlite3_column_value(pIter->pSelect, i);
|
||||
sqlite3_bind_value(pUpdate, i+1, pVal);
|
||||
}
|
||||
if( pIter->bVtab ){
|
||||
if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
|
||||
/* Bind the ota_rowid value to column _rowid_ */
|
||||
assertColumnName(pIter->pSelect, pIter->nCol+1, "ota_rowid");
|
||||
pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
|
||||
sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
|
||||
}
|
||||
@ -1195,7 +1263,7 @@ int sqlite3ota_step(sqlite3ota *p){
|
||||
/* Clean up the ota_tmp_xxx table for the previous table. It
|
||||
** cannot be dropped as there are currently active SQL statements.
|
||||
** But the contents can be deleted. */
|
||||
if( pIter->bVtab==0 ){
|
||||
if( pIter->eType!=OTA_PK_VTAB ){
|
||||
otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl);
|
||||
}
|
||||
}else{
|
||||
|
16
manifest
16
manifest
@ -1,5 +1,5 @@
|
||||
C Update\sota\sso\sthat\sthe\shidden\scolumns\sof\svirtual\stables\smay\sbe\swritten.
|
||||
D 2014-11-27T18:09:46.630
|
||||
C Allow\sthe\sota\sextension\sto\swrite\sto\stables\swith\sno\sPRIMARY\sKEY\sdeclaration.
|
||||
D 2014-12-06T19:30:41.673
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -125,7 +125,7 @@ F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/ota/README.txt 78d4a9f78f567d4bf826cf0f02df6254902562ca
|
||||
F ext/ota/ota.c c11a85af71dccc45976622fe7a51169a481caa91
|
||||
F ext/ota/ota1.test a8f9d89c9b2d381a663bcedaa5dd5952cdbd1231
|
||||
F ext/ota/ota1.test 64770d76d3dc00c24f9a78ac69e4448708bde985
|
||||
F ext/ota/ota10.test ab815dff9cef7248c504f06b888627d236f25e9c
|
||||
F ext/ota/ota2.test 4568c2671d19dbde789fb9091d727a2e94880128
|
||||
F ext/ota/ota3.test 71bd8cc0cf8d7e7d9bb11a1fcc238320a5a9d8c8
|
||||
@ -134,9 +134,9 @@ F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
|
||||
F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
|
||||
F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
|
||||
F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
|
||||
F ext/ota/ota9.test d9ad30ccb4e08f878e382876fe67752309538af9
|
||||
F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
|
||||
F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
|
||||
F ext/ota/sqlite3ota.c f1e930690ec2bcb72138301ebf05ea2ffd4450de
|
||||
F ext/ota/sqlite3ota.c 84cab0f965144772068ec0183252ae5e5278f0be
|
||||
F ext/ota/sqlite3ota.h 8dc9c812e61f47f497336a45f820bee88b33a2c5
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
|
||||
@ -1237,7 +1237,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
|
||||
R 19cf180a791a327f1f4fb68723a586ef
|
||||
P ccee999649d0fa1d48e53847542f4cbe05e3d694
|
||||
R 23b9af0814440584d410f9d611aafb5a
|
||||
U dan
|
||||
Z 7646a22500cce7c7ef211b057e34ff27
|
||||
Z b3bc69908e00e34016dd9eb2dbbe05c2
|
||||
|
@ -1 +1 @@
|
||||
ccee999649d0fa1d48e53847542f4cbe05e3d694
|
||||
ba59a7e2ba97244492cbca9247456df0f3f19248
|
Loading…
Reference in New Issue
Block a user