Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   PHP (http://www.programmingforums.org/forum29.html)
-   -   An Attempt at a DBMS (http://www.programmingforums.org/showthread.php?t=13008)

grimpirate Apr 17th, 2007 12:06 AM

An Attempt at a DBMS
 
My current project which is my forum software board uses a flat file database in order to store the forum's data (user data, post data, etc.). I grew curious and starting wondering how actual databases work, and realized that the grimBase class I created is a sort of pseudo-relational database. However, what I always seem to read is that irrelevant of the database, data is stored in tables in a column and row format of some sort. I don't know how the actual implementations go, but I wanted to address a problem in my implementation. I would imagine this falls under the category of creating locks for a database. So here's the code for the grimBase class.[PHP]<?php
echo '<pre>';
$blah = new grimBase('foo.php');
$blah->generateGlyph(array(0, 1, 2, 3, 4));
print_r($blah->parseGlyph());
echo '</pre>';

class grimBase{
var $gbArtefact;
var $gbTablet;
var $gbGlyph;
var $gbArtefactExists = false;
var $gbIsTablet = false;
var $gbGlyphData = null;

function grimBase($artefactID){
if(!isset($artefactID)) trigger_error("grimBase ( string filename) &rarr; Undefined argument filename", E_USER_ERROR);
if(!is_string($artefactID)) trigger_error("grimBase ( string filename) &rarr; filename is an incorrect type", E_USER_ERROR);
if(strlen($artefactID) == 0) trigger_error("grimBase ( string filename) &rarr; filename cannot be an empty string", E_USER_ERROR);
$slashPos = strpos($artefactID, '/');
if($slashPos === 0 || $slashPos === strlen($artefactID) - 1) trigger_error("grimBase ( string filename) &rarr; filename cannot start with a / or end with a /", E_USER_ERROR);
$this->gbArtefact = $artefactID;
$this->gbArtefactExists = file_exists($this->gbArtefact);
$this->gbIsTablet = is_dir($artefactID);
$gbTemp = explode('/', $artefactID);
$dotPos = strpos(end($gbTemp), '.');
if($dotPos !== false && $dotPos !== strlen(current($gbTemp)) - 1){
$this->gbGlyph = array_pop($gbTemp);
}else{
$this->gbGlyph = '';
}
if(count($gbTemp) > 1){
$this->gbTablet = implode('/', $gbTemp);
}else{
$this->gbTablet = reset($gbTemp);
}
}

function artefact(){
return $this->gbArtefact;
}

function tablet(){
return $this->gbTablet;
}

function glyph(){
return $this->gbGlyph;
}

function artefactExists(){
return $this->gbArtefactExists;
}

function artefactCount(){
if(!$this->gbArtefactExists) return !trigger_error("int grimBase->artefactCount ( void) &rarr; Tablet does not exist", E_USER_ERROR);
return count($this->getArtefacts());
}

function getArtefacts(){
if(!$this->gbArtefactExists) return !trigger_error("array grimBase->getArtefacts ( void) &rarr; Tablet does not exist", E_USER_ERROR);
$far = array();
if($handle = opendir($this->gbTablet)){
while(false !== ($file = readdir($handle))){
if($file != "." && $file != ".."){
array_push($far, $file);
}
}
closedir($handle);
}
return $far;
}

function generateTablet(){
if(file_exists($this->gbTablet)) return true;
if(fluctuate($this->gbTablet, 0777)){
$this->gbArtefactExists = true;
}
return $this->gbArtefactExists;
}

function generateGlyph($glyphData){
if(!isset($glyphData)) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; Undefined argument &array", E_USER_ERROR);
if(!is_array($glyphData)) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; &array is an incorrect type", E_USER_ERROR);
if(count($glyphData) == 0) return !trigger_error("bool grimBase->generateGlyph ( array &array) &rarr; &array is empty", E_USER_ERROR);
$this->gbGlyphData = $glyphData;
$fout = '<?php /*' . chr(30);
foreach($glyphData as $glyphKey => $glyphValue){
$fout .= $glyphKey;
$fout .= ' = ';
$fout .= serialize($glyphValue);
$fout .= chr(30);
}
$fout .= '*/ ?>';
if(fluctuate($this->gbArtefact, 'w', $fout, 0777)){
$this->gbArtefactExists = true;
$this->gbIsTablet = false;
$this->gbGlyphData = $glyphData;
return true;
}else{
$this->gbArtefactExists = false;
$this->gbGlyphData = null;
return false;
}
}

function scanGlyph($glyphID){
if(!$this->gbArtefactExists) return !trigger_error("mixed grimBase->scanGlyph ( string arrayKey) &rarr; Glyph does not exist", E_USER_ERROR);
if($this->gbIsTablet) return !trigger_error("mixed grimBase->scanGlyph ( string arrayKey) &rarr; Cannot parse a tablet", E_USER_ERROR);
if(!isset($this->gbGlyphData)){
$this->gbGlyphData = $this->parseGlyph();
}
return $this->gbGlyphData[$glyphID];
}

function parseGlyph(){
if(!$this->gbArtefactExists) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Glyph does not exist", E_USER_ERROR);
if($this->gbIsTablet) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Cannot parse a tablet", E_USER_ERROR);
$parseContent = file_get_contents($this->gbArtefact);
if(false === $parseContent) return !trigger_error("array grimBase->parseGlyph ( void) &rarr; Unable to retrieve glyph contents", E_USER_ERROR);
$contentParser = explode(chr(30), $parseContent);
$parseContent = array();
array_shift($contentParser);
array_pop($contentParser);
reset($contentParser);
foreach($contentParser as $contentValue){
$ePos = strpos($contentValue, '=');
$parseKey = trim(substr($contentValue, 0, $ePos - 1));
$parseValue = unserialize(substr($contentValue, $ePos + 2));
$parseContent[$parseKey] = $parseValue;
}
$this->gbGlyphData = $parseContent;
return $parseContent;
}
}

function fluctuate(){
$args = func_get_args();
switch(func_num_args()){
case 1:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(is_file($args[0])){
if(unlink($args[0])) return true;
else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to delete file", E_USER_WARNING);
}elseif(is_dir($args[0])){
if(rmdir($args[0])) return true;
else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to delete folder", E_USER_WARNING);
}else return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Nonexistent file/directory cannot be deleted", E_USER_NOTICE);
case 2:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(!is_int($args[1])) return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; <em>mode</em> is not an int", E_USER_ERROR);
if(mkdir($args[0], $args[1])){
return true;
}else{
return !trigger_error("bool fluctuate ( string dirname [, int mode]) &rarr; Unable to create directory", E_USER_ERROR);
}
case 4:
if(!is_string($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>filename</em> is not a string", E_USER_ERROR);
if(!is_string($args[1]) || strlen($args[1]) !== 1) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>mode</em> is not a character", E_USER_ERROR);
if(!is_int($args[3])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; <em>mode</em> is not an int", E_USER_ERROR);
$args[1] = strtolower($args[1]);
$ffd = null;
switch($args[1]){
case 'a':
if(!file_exists($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Cannot append to nonexistent file", E_USER_WARNING);
if(!is_writeable($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; File is not writeable", E_USER_WARNING);
if(false === ($ffd = fopen($args[0], "a"))) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to open file", E_USER_WARNING);
break;
case 'w':
if(false === ($ffd = fopen($args[0], "w"))) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to open file", E_USER_WARNING);
if(!file_exists($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Cannot write to nonexistent file", E_USER_WARNING);
if(!is_writeable($args[0])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; File is not writeable", E_USER_WARNING);
break;
default:
return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Illegal mode specified", E_USER_ERROR);
}
if(stream_set_write_buffer($ffd, 0) !== -1) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to buffer stream", E_USER_WARNING);
if(fwrite($ffd, $args[2]) === false) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to write to file", E_USER_WARNING);
if(!fclose($ffd)) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to close file", E_USER_WARNING);
if(!chmod($args[0], $args[3])) return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Unable to change file permissions", E_USER_WARNING);
return true;
default:
return !trigger_error("bool fluctuate ( string filename [, string mode [, string string [, int mode]]]) &rarr; Incorrect quantity of arguments supplied", E_USER_ERROR);
}
}
?>[/PHP]Now to compare actual database tables to my own there's 3 things to know about the grimBase class. It is composed of what I call Artefacts, Tablets, and Glyphs. Tablets and Glyphs are both Artefacts. On your local computer Tablets are the equivalent of folders/directories and Glyphs are ascii text files. So for instance when I want to create a database table (what I call a Tablet) I would code something like
:

$foo = new grimBase('foo');
$foo->generateTablet();

Now if I wanted to add an row to the table I would code something like the following
:

$foo = new grimBase('foo/0.php');
$foo->generateGlyph(array(0, 1, 2, 3, 4));

And that adds a row to the table (the row itself is what I call a Glyph). Therefore a database table with various entries in it in my grimBase is equivalent to a Tablet with various Glyphs in it. Physically this is a folder with various files in it. Now as far as writing to the same Glyph at the exact same time that can't happen because of the set_stream_write_buffer function. However, let's say two users want to reply to a topic in the forum at the same time, the forum automatically creates a Glyph reference to the next needed file, so now two users are referencing the same Glyph record. So now an overwrite of one of the two user's posts can occur. Whoever submits their post last I would imagine. There's an intermediate step where a final check is performed to ensure that that post hasn't already been created, but I imagine there's still a possibility that due to timing the post may still get overriden. So my question is how can I possibly create a lock to prevent that from happening? Or am I looking at this database thing all wrong in terms of the implementation? Should I rather than using individual files use one file and then just add rows? I realize this may be more complicated than what I think, and I have been reading on the subject and book on DBMS where the author stipulates these locks are necessary and I understand why, it's just it's a conceptual book and doesn't actually deal with the physical aspect of programming it.

DaWei Apr 17th, 2007 12:26 AM

There's nothing wrong with experimenting around, in the dark, in an attempt to invent databases. I would suggest that you would be well ahead of the game if you learned how current databases work and experimented around, in the light, in an attempt to improve on them. Just personal opinion.

grimpirate Apr 17th, 2007 12:59 AM

I agree DaWei, I'm not trying to reinvent the wheel or anything. I'm reading the book Database Management Systems 2nd Ed. from McGraw Hill. It talks about a great deal of things that I can't really seem to find much info about online. Usually all I manage to end up googling is someone explaining SQL or how SQL works to get stuff done, how the tables look, etc. It's all very kind of separated from the code that creates the database. There's sqllite, but that's written in C and my grasp of C is limited. Furthermore, I'm pretty weak at reading other's code. I'm not actually trying to improve databases. It's just that I use free webservers so it would be nice to have one that's free. There's one that I found which is txtSQL which I have yet to look into further, but it's always very difficult to study someone else's code if it isn't explained entirely.

kruptof Apr 17th, 2007 5:52 AM

What you need to do is find out the functions of a DBMS, for example:
Store, retrieve and update data
Transaction Services
Data-independence
Data integrity service
Security service
Concurrency control service
Data Communication Service
Recovery and Backup services

Also I think there is something called the ANSI-SPARC Three Layer Model that you can follow to help with the design and hopefully the implementation of your DBMS

DaWei Apr 17th, 2007 8:56 AM

If you have a machine at home, I'd recommend you put an AMP package on it and play with that. Then you'll know how to use a DB if/when you have a host that supports it.

Writing a DB has two aspects. One is the functionality. You can learn to write that in any language, but the end result will likely be missing the other aspect: performance. Performance is attained by writing code that is very system specific, close to the machine.

Infinite Recursion Apr 17th, 2007 9:55 AM

Quote:

It's just that I use free webservers so it would be nice to have one that's free.
MySQL is free. http://www.mysql.com/

Kudos on your effort. Makes me want to write a web application in PHP now.

grimpirate Apr 17th, 2007 1:38 PM

@kruptof: Hmm... browsed around for ANSI - SPARC but I mostly find cryptic definitions. All those concepts you mention are addressed in the book I'm reading, along with ACID and many others.

@DaWei: When I search for AMP package on Google all I get are like actual guitar amps lol, sorry I don't know what the acronym stands for. I've already worked with databases and understand how to use the SQL language and such. However, that's very separated from the actual physical things going on in a database,and of course performance is undoubtedly a crucial feature of a database. However, due to my free webhost constraint performance is secondary. I would venture to hypothesize that perhaps using the PHP filesystem functions and the like might be quicker than actually logging into the database and polling it over and over, at least online that is.

@Infinite Recursion: lol yeah I know that MySQL is free and open source, that isn't what I mean though. Your host still has to grant you database access to use MySQL, and a lot of free webhosts don't. Happy I could get you motivated ^_^ How about a mod for GrimBB? SHAMELESS PLUG! lol

@anyone still reading: I did find this other pretty impressive flat file database http://gladius.sourceforge.net/. That's pretty much what I'm trying to create. However, looking at their source code gives me a headache *_* lol Obviously those people know what they're doing as I ignorantly assume good code gives me a headache.
Regarding the problem that I was stating earlier I think I may have come up with a solution. When a user decides to make a post I should immediately create a Glyph which indicates that someone is accessing the Tablet in order to create a post. Perhaps this Glyph could contain a queue of sorts, so as to process each user's post systematically. I assume that if the same thing where to happen here on PFO the thread wouldn't update dynamically if a post was made right now while I'm typing this one.

kruptof Apr 17th, 2007 2:00 PM

here is what i think Dawei meant http://www.wampserver.com/en/, good for practising and testing your project on.

DaWei Apr 17th, 2007 2:01 PM

AMP is Apache/MySql/PHP. It's a very easy thing to implement. You can also find binary packages the do the entire installation, but I don't like them as well.

This kind of setup allows you to play on your home machine, regardless of what your host provides. You can also add things like Perl to the mix.


All times are GMT -5. The time now is 2:08 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC