1
/* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3
* This program is free software; you can redistribute it and/or modify
4
* it under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2 of the License, or
6
* (at your option) any later version.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
* $Id: db_file.pike,v 1.1.1.1 2006/03/27 12:40:10 exodusd Exp $
20
constant cvs_version="$Id: db_file.pike,v 1.1.1.1 2006/03/27 12:40:10 exodusd Exp $";
25
* this is the database file emulation, which stores a binary file
26
* in a sequence of 64k blobs in a SQL database.
32
private static int iNextRecNbr; /* last current read */
33
private static int iCurrPos; /* current position */
34
private static int iMaxRecNbr; /* last rec_order block */
35
private static int iMinRecNbr; /* first rec_order block */
36
private static string sMode; /* read/write */
37
private static string sReadBuf="";
38
private static string sWriteBuf="";
39
private static int iFileSize=-1; /* not set otherwise >=0 */
40
private static object readRecord;
41
private static int iStopReader=0;
42
private static Thread.MutexKey mReader;
43
private static int iPrefetch=1;
45
private static int iLastAccess;
47
array get_database_handle(int id)
49
return _Database->connect_db_file(id);
52
void create(int ID, string mode) {
59
* open a database content with given ID, if ID 0 is given a new ID
62
* @param int ID - (an Content ID | 0)
63
* @param string mode -
64
* 'r' open file for reading
65
* 'w' open file for writing
66
* 'a' open file for append (use with 'w')
67
* 't' truncate file at open (use with 'w')
68
* 'c' create file if it doesn't exist (use with 'w')
69
* 'x' fail if file already exist (use with 'c')
71
* How must _always_ contain exactly one 'r' or 'w'.
72
* if no ID is given, mode 'wc' is assumed
73
* 'w' assumes 'a' unless 't'
76
* @return On success the ID (>1) -- 0 otherwise
78
* @author Ludger Merkens
81
int open(int ID, string mode) {
84
Sql.sql_result odbResult; // db = iID >> OID_BITS;
88
[fdb, iID] = get_database_handle(iID);
90
// LOG("opened db_file for mode "+sMode+" with id "+iID);
93
if (search(sMode, "r")!=-1)
96
fdb()->big_query("select min(rec_order), max(rec_order) "+
97
"from doc_data where "+
99
array res= odbResult->fetch_row();
100
iMinRecNbr= (int) res[0];
101
iMaxRecNbr= (int) res[1]; // both 0 if FileNotFound
102
iNextRecNbr = iMinRecNbr;
105
fdb()->big_query("select rec_data from doc_data where doc_id="+iID+
106
" and rec_order="+iMinRecNbr);
107
if (odbResult->num_rows()==1)
109
[sReadBuf] = odbResult->fetch_row();
110
if (strlen(sReadBuf)<MAX_BUFLEN) // we got the complete file
111
iFileSize = strlen(sReadBuf);
113
iPrefetch = 1; // otherwise assume prefetching
119
if (search(sMode, "w")==-1) // neither read nor write mode given
122
// Append to database, calculate next RecNbr
123
odbResult = fdb()->big_query("select max(rec_order) from "+
124
"doc_data where doc_id = "+iID);
125
if (!objectp(odbResult))
129
iNextRecNbr = ((int) odbResult->fetch_row()[0])+1;
131
if (search(sMode, "c")!=-1)
133
if ((search(sMode,"x")!=-1) && (iNextRecNbr != -1))
136
if (iNextRecNbr == -1)
140
if (search(sMode, "t")!=-1)
143
fdb()->big_query("delete from doc_data where doc_id = "+
148
if (iNextRecNbr == -1) // 'w' without 'c' but file doesn't exist
154
private static void write_buf(string data) {
155
// LOG_DB("write_buf: "+strlen(data)+" RecNbr:"+iNextRecNbr);
156
string line = "insert into doc_data values('"+
157
fdb()->quote(data)+"', "+ iID +", "+iNextRecNbr+")";
158
mixed c = catch{fdb()->big_query(line);};
160
LOG_DB("write_buf: "+c[0]+"\n"+master()->describe_backtrace(c[1]));
162
iMaxRecNbr=iNextRecNbr;
168
// werror("closing db_file"+iID+" from mode "+sMode);
169
if (search(sMode,"w")!=-1)
171
if (strlen(sWriteBuf) > 0)
172
write_buf(sWriteBuf);
173
iFileSize = (((iMaxRecNbr - iMinRecNbr)-1) * MAX_BUFLEN) +
183
static int sendByte = 0;
185
int get_last_access() { return iLastAccess; }
187
static void check_status(object record)
190
mlock = record->fullMutex->lock();
191
if ( functionp(record->restore) ) {
192
record->restore(record);
199
string read(int|void nbytes, int|void notall)
201
array(string) lbuf = ({});
204
Sql.sql_result odbData;
207
if ( search(sMode,"r") == -1 )
210
iLastAccess = time();
213
if (!nbytes) // all the stuff -> no queuing
215
odbData = fdb()->big_query("select rec_data from doc_data "+
217
" order by rec_order");
218
while (line = odbData->fetch_row())
219
lbuf += ({ line[0] });
222
else if ( !objectp(readRecord) &&
223
(iFileSize == -1 || iFileSize> MAX_BUFLEN ) && iPrefetch )
225
readRecord = _Database->read_from_database(iID,
231
iSumLen = strlen(sReadBuf);
232
lbuf = ({ sReadBuf });
234
while ( iSumLen < nbytes && stringp(line) )
236
if ( readRecord ) // check for Prefetched Content
238
check_status(readRecord);
239
line = readRecord->contFifo->read();
241
else if (iNextRecNbr < iMaxRecNbr) // large files + seek
244
readRecord = _Database->read_from_database(iID,
248
check_status(readRecord);
249
line = readRecord->contFifo->read();
252
line = 0; // small files
257
iSumLen += strlen(line);
265
sReadBuf = lbuf * "";
267
if (!strlen(sReadBuf))
270
if (strlen(sReadBuf) <= nbytes) // eof or notall
274
iCurrPos += strlen(line);
275
sendByte += strlen(line);
279
line = sReadBuf[..nbytes-1];
280
sReadBuf = sReadBuf[nbytes..];
281
iCurrPos += strlen(line);
282
sendByte += strlen(line);
286
int write(string data) {
289
if (search(sMode, "w")==-1)
293
while (strlen(sWriteBuf) >= MAX_BUFLEN)
295
write_buf(sWriteBuf[..MAX_BUFLEN-1]);
296
sWriteBuf = sWriteBuf[MAX_BUFLEN..];
297
iWritten += MAX_BUFLEN;
299
iCurrPos += iWritten;
305
object s = Stdio.Stat();
312
if (iFileSize!=-1) // already calculated
318
if (search(sMode, "w")!=-1)
321
iLastChunkLen = strlen(sWriteBuf);
323
erg = ((iMaxRecNbr-iMinRecNbr) * MAX_BUFLEN) + iLastChunkLen;
328
res = fdb()->big_query(
329
"select length(rec_data) from doc_data "+
330
"where doc_id ="+iID+" and rec_order="+iMaxRecNbr);
332
mixed row = res->fetch_row();
334
iLastChunkLen = ((int)row[0]);
339
iFileSize = ((iMaxRecNbr-iMinRecNbr) * MAX_BUFLEN) + iLastChunkLen;
348
private static void stop_reader()
350
if ( objectp(readRecord) ) {
351
//werror("Trying to stop read operation on "+ readRecord->iID+"\n");
352
readRecord->stopRead = 1;
357
* seek in an already open database content to a specific offset
358
* If pos is negative it will be relative to the start of the file,
359
* otherwise it will be an absolute offset from the start of the file.
361
* @param int pos - the position as described above
362
* @return The absolute new offset or -1 on failure
365
* @caveats The old syntax from Stdio.File->seek with blocks is not
373
Sql.sql_result odbResult;
376
//werror("Seek(%d)\n", pos);
379
SeekPos = iCurrPos-SeekPos;
382
SeekBlock = SeekPos / MAX_BUFLEN;
383
SeekBlock += iMinRecNbr;
385
stop_reader(); // discard prefetch and stop read_thread
386
odbResult = fdb()->big_query("select rec_data from doc_data where doc_id="+
387
iID+" and rec_order="+SeekBlock);
388
if (odbResult->num_rows()==1)
390
[sReadBuf] = odbResult->fetch_row();
391
sReadBuf = sReadBuf[(SeekPos % iMinRecNbr)..];
393
iNextRecNbr = SeekBlock+1;
400
* tell the current offset in an already open database content
401
* @return The absolute offset
411
return "kernel/db_file(id="+iID+", stopped="+iStopReader+")";