A Toy SQL Database in JavaScript

The code shown below implements an unfinished, extremely inefficient, buggy, and completely useless SQL database that runs in JavaScript in a web browser. To see it in action, copy the code to an .html file and open that file in a web browser.

I’m really just putting it here as a placeholder for further work. As I say, it doesn’t do too much at present other than running a single, simple, pre-defined query. Of course, even if it worked perfectly and had a million features, it’s doubtful that there’s ever going to be much need for a database that actually runs on the client side in a web browser.

UPDATE 2017/03/31 – I have done some more work on this database, and refactored it significantly. It now allows the user to create databases, define tables, insert values into them, and perform very simple queries. It still currently can’t do joins, updates, or deletes.


<html>
<body>

<!-- ui -->
<div>
	<div>
		<div><label>Command:</label></div>
		<div>
			<textarea id="textareaCommand" cols="80" rows="25">

-- Create the database and table.

create database TestDatabase;
use database TestDatabase;
create table TestTable as TestID int, TestValue text;

-- Insert some rows into the table.

insert into TestTable select 1, 'One';
insert into TestTable select 2, 'Two';
insert into TestTable select 3, 'Three';
insert into TestTable select 4, 'Four';
insert into TestTable select 5, 'Five';

-- Select all columns of all rows.

select * from TestTable;

-- Update some rows and select the changes.

update TestTable set TestValue = 'Too Much' where TestID > 3;
select TestValue from TestTable where TestID = 1;

			</textarea>
		</div>
		<div><button onclick="Globals.Instance.managementInterface.executeCommands();">Execute</button></div>
	</div>
	
	<div>
		<div><label>Results:</label></div>
		<div><textarea id="textareaResults" cols="80" rows="20"></textarea></div>
	</div>

</div>

<script type="text/javascript">

"use strict";

// main

function main()
{
	var datatypes = Datatype.Instances;

	var server = new Server
	(
		"Server0", []
	);

	var managementInterface = new ManagementInterface();

	Globals.Instance.initialize
	(
		server,
		managementInterface
	);
}

// extensions

function ArrayExtensions()
{
	// extension class
}
{
	Array.prototype.addLookups = function(keyName)
	{
		for (var i = 0; i < this.length; i++)
		{
			var element = this[i];
			var key = element[keyName];
			this[key] = element;
		}

		return this;
	}

	Array.prototype.remove = function(elementToRemove)
	{
		var elementIndex = this.indexOf(elementToRemove);
		if (elementIndex >= 0)
		{
			this.splice(elementIndex, 1);
		}

		return this;
	}
}

function StringExtensions()
{
	// extension class
}
{
	String.prototype.isLetter= function()
	{
		return (this.toLowerCase() != this.toUpperCase());
	}

	String.prototype.padRight = function(lengthToPadTo, stringToPadWith)
	{
		var returnValue = this;

		while (returnValue.length < lengthToPadTo)
		{
			returnValue += stringToPadWith;
		}

		return returnValue;
	}

	String.prototype.removeExtraSpaces = function()
	{
		var returnValue = this;

		while (returnValue.indexOf("  ") == true)
		{
			returnValue = this.replace("  ", " ");
		}

		return returnValue;
	}

	String.prototype.replace = function(stringToBeReplaced, stringToReplaceWith)
	{
		return this.split(stringToBeReplaced).join(stringToReplaceWith);
	}

	String.prototype.removeExtraWhitespace = function()
	{
		return this.replace
		(
			"\r", " "
		).replace
		(
			"\n", " "
		).replace
		(
			"\t", " "
		).removeExtraSpaces();
	}
}

// classes

function ColumnDefn(name, datatype)
{
	this.name = name;
	this.datatype = datatype;

	this.indexWithinTable = null;
}
{
	ColumnDefn.prototype.clone = function()
	{
		var returnValue = new ColumnDefn
		(
			this.name, this.datatype, this.dataLength
		);

		return returnValue;
	}
}

function ColumnReference(tableName, columnName)
{
	this.tableName = tableName;
	this.columnName = columnName;
}
{
	ColumnReference.prototype.toString = function()
	{
		var returnValue = (this.tableName == null ? "" : this.tableName + ".");
		returnValue += this.columnName;
		return returnValue;
	}
}

function Command()
{
	// do nothing
}
{
	// static methods

	Command.parse = function(commandAsString)
	{
		var returnValue = null;

		var server = Globals.Instance.server;
		var databases = server.databases;
		var databaseCurrent = server.databaseCurrent;

		commandAsString = commandAsString.removeExtraWhitespace();

		var tokens = commandAsString.split(" ");
		var token0 = tokens[0];

		if (token0 == "alter")
		{
			returnValue = CommandTableAlter.parse(tokens);
		}
		else if (token0 == "create")
		{
			returnValue = Command.parse_Create(tokens);
		}
		else if (token0 == "delete")
		{
			returnValue = CommandTableDelete.parse(tokens);
		}
		else if (token0 == "describe")
		{
			returnValue = Command.parse_Describe(tokens);
		}
		else if (token0 == "drop")
		{
			returnValue = Command.parse_Drop(tokens);	
		}
		else if (token0 == "insert")
		{
			returnValue = CommandTableInsert.parse(tokens);
		}
		else if (token0 == "select")
		{
			returnValue = CommandTableSelect.parse(tokens);
		}
		else if (token0 == "update")
		{
			returnValue = CommandTableUpdate.parse(tokens);
		}
		else if (token0 == "use")
		{
			returnValue = CommandDatabaseUse.parse(tokens);
		}
		else 
		{
			returnValue = new CommandError("Unrecognized command: '" + token0 + "'");
		}

		return returnValue;			
	}

	Command.parse_Create = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "database")
		{
			returnValue = CommandDatabaseCreate.parse(tokens);
		}
		else if (token1 == "table")
		{
			returnValue = CommandTableCreate.parse(tokens);
		}
		else
		{
			returnValue = new CommandError
			(
				"Expected 'database' or 'table' after " + token0
			);
		}

		return returnValue;
	}

	Command.parse_Describe = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "server")
		{
			returnValue = new CommandServerDescribe("");
		}
		else if (token1 == "database")
		{
			returnValue = new CommandDatabaseDescribe("");
		}
		else if (token1 == "table")
		{
			returnValue = new CommandTableDescribe("");
		}
		else
		{
			returnValue = new CommandError
			(
				"Expected 'server', 'database' or 'table'."
			);
		}

		return returnValue;
	}

	Command.parse_Drop = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "database")
		{
			returnValue = CommandDatabaseDrop.parse(tokens);
		}
		else if (token1 == "table")
		{
			returnValue = CommandTableDrop.parse(tokens);
		}
		else
		{
			returnValue = new CommandError("Unrecognized command: " + token0 + " " + token1);
		}

		return returnValue;
	}

	Command.titleAndMessageToTable = function(title, message)
	{
		var messageAsTable = new Table
		(
			title, 
			new TableDefn
			([
				new ColumnDefn("Result:", Datatype.Instances.Varchar, null, false),
			]),
			
			[
				new Row( [ message ] ),
			]
		);

		return messageAsTable;
	}
}

function CommandDatabaseCreate(databaseName)
{
	this.databaseName = databaseName;
}
{
	CommandDatabaseCreate.parse = function(tokens)
	{
		var databaseName = tokens[2];
		var returnValue = new CommandDatabaseCreate(databaseName);
		return returnValue;
	}

	CommandDatabaseCreate.prototype.execute = function()
	{
		var server = Globals.Instance.server;
		var databaseNew = new Database(this.databaseName, []);
		server.databaseAdd(databaseNew);
		var returnValue = Command.titleAndMessageToTable
		(
			"create database " + this.databaseName, 
			"Database '" + this.databaseName + "' created."
		);
		return returnValue;
	}
}

function CommandDatabaseDescribe()
{
	// do nothing	
}
{
	CommandDatabaseDescribe.prototype.execute = function()
	{
		var tablesAsTable = new Table
		(
			"tables",
			new TableDefn
			([
				new ColumnDefn("name"),
			])
		);

		var rows = [];

		var server = Globals.Instance.server;
		var database = server.databaseCurrent;
		var tables = database.tables;

		for (var i = 0; i < tables.length; i++)
		{
			var table = tables[i];	
			var tableName = table.name;
			var tableAsRow = new Row([tableName]);
			rows.push(tableAsRow);
		}

		return tablesAsTable;
	}
}

function CommandDatabaseDrop(databaseName)
{
	this.databaseName = databaseName;
}
{
	CommandDatabaseDrop.prototype.parse = function(tokens)
	{
		var returnValue;

		var server = Globals.Instance.server;

		var databaseName = tokens[2];
		var database = server.databases[databaseName];
		if (database == null)
		{
			var errorMessage = 
				"No database exists named '" 
				+ databaseName + 
				"'.  Try the 'create database' command first.";

			returnValue = new CommandError(errorMessage);
		}
		else
		{
			returnValue = new CommandDatabaseDrop(databaseName);
		}

		return returnValue;
	}

	CommandDatabaseDrop.prototype.execute = function()
	{
		return Command.titleAndMessageToTable("drop database", "Not yet implemented.");
	}
}

function CommandDatabaseUse(databaseName)
{
	this.databaseName = databaseName;
}
{
	CommandDatabaseUse.parse = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		var databaseName;
		if (token1 == "database")
		{
			databaseName = tokens[2];
		}
		else
		{
			databaseName = token1;
		}

		var server = Globals.Instance.server;
		var database = server.databases[databaseName];

		if (database == null)
		{
			returnValue = new CommandError("No database exists named '" + databaseName + "'.");
		}
		else
		{
			returnValue = new CommandDatabaseUse(databaseName);
		}

		return returnValue;
	}

	CommandDatabaseUse.prototype.execute = function()
	{
		var server = Globals.Instance.server;
		var database = server.databases[this.databaseName];
		server.databaseCurrent = database;
		return Command.titleAndMessageToTable
		(
			"use database " + this.databaseName, 
			"The current database is now '" + this.databaseName + "'."
		);
	}
}

function CommandError(message)
{
	this.message = message;
}
{
	CommandError.prototype.execute = function()
	{
		return Command.titleAndMessageToTable("Error", this.message);
	}
}

function CommandServerDescribe()
{
	// do nothing
}
{
	CommandServerDescribe.prototype.execute = function()
	{
		var databasesAsTable = new Table
		(
			"describe server",
			new TableDefn
			([
				new ColumnDefn("database"),
			])
		);

		var rows = [];

		var databases = Globals.Instance.server.databases;

		for (var i = 0; i < databases.length; i++)
		{
			var database = databases[i];	
			var databaseName = database.name;
			var databaseAsRow = new Row([databaseName]);
			rows.push(databaseAsRow);
		}

		return databasesAsTable;
	}
}

function CommandTableAlter()
{
	// todo
}
{
	CommandTableAlter.parse = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "table")
		{
			returnValue = new CommandTableAlter();
		}
		else
		{
			returnValue = new CommandError("Unrecognized command: " + token0 + " " + token1);
		}

		return returnValue;
	}

	CommandTableAlter.prototype.execute = function()
	{
		return Command.titleAndMessageToTable("alter table", "Not yet implemented.");
	}
}

function CommandTableCreate(tableName, columnDefns)
{
	this.tableName = tableName;
	this.columnDefns = columnDefns;
}
{
	CommandTableCreate.parse = function(tokens)
	{
		var returnValue;

		var databaseCurrent = Globals.Instance.server.databaseCurrent;

		if (databaseCurrent == null)
		{
			var errorMessage = 
				"No database currently selected."
				+ "  Try the 'use database' command first.";

			returnValue = new CommandError(errorMessage);				
		}
		else
		{
			var tableName = tokens[2];
			var table = databaseCurrent.tables[tableName];

			if (table != null)
			{
				returnValue = new CommandError
				(
					"A table already exists named '" + tableName + "'."
				);
			}
			else
			{
				var token3 = tokens[3];
				if (token3 == "as")
				{
					var columnDefns = [];
					var datatypes = Datatype.Instances;
	
					for (var t = 4; t < tokens.length; t += 2)
					{
						var columnName = tokens[t];
						var columnDatatypeName = tokens[t + 1];
						var columnDatatype = 
							datatypes[columnDatatypeName];
						var columnDefn = new ColumnDefn
						(
							columnName,
							columnDatatype
						);
						columnDefns.push(columnDefn);
					}

					returnValue = new CommandTableCreate
					(
						tableName, columnDefns
					);
				}
				else
				{
					returnValue = new CommandError
					(
						"Expected 'as' after table name."
					);
				}
			}
		}

		return returnValue;
	}

	CommandTableCreate.prototype.execute = function()
	{
		var table = new Table
		(
			this.tableName,

			new TableDefn(this.columnDefns),

			[] // rows
		);

		var database = Globals.Instance.server.databaseCurrent;
		database.tableAdd(table);

		var returnValue = Command.titleAndMessageToTable
		(
			"create table " + this.tableName, 
			"Table '" + this.tableName + "' created."
		);

		return returnValue;
	}
}

function CommandTableDelete(tableName, conditions)
{
	this.tableName = tableName;
	this.conditions = conditions;
}
{
	CommandTableDelete.parse = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "from")
		{
			var tableName = tokens[2];
			var table = databaseCurrent.tables[tableName];
			if (table == null)
			{
				returnValue = new CommandError("No table exists named " + tableName);
			}
			else
			{
				returnValue = new CommandTableDelete();
			}
		}
		else
		{
			returnValue = new CommandError("Unrecognized command: " + token0 + " " + token1);
		}

		return returnValue;
	}

	CommandTableDelete.prototype.execute = function()
	{
		// todo
	}
}

function CommandTableDescribe(tableName)
{
	this.tableName = tableName;
}
{
	CommandTableDescribe.prototype.execute = function()
	{
		var columnDefnsAsTable = new Table
		(
			"columns",
			new TableDefn
			([
				new ColumnDefn("name"),
			])
		);

		var rows = [];

		var server = Globals.Instance.server;
		var database = server.databaseCurrent;
		var tables = database.tables;
		var table = tables[this.tableName];
		var columnDefns = table.defn.columnDefns;

		for (var i = 0; i < columnDefns.length; i++)
		{
			var columnDefn = columnDefns[i];	
			var columnDefnName = columnDefn.name;
			var columnDefnAsRow = new Row([columnDefnName]);
			rows.push(columnDefnAsRow);
		}

		return columnDefnsAsTable;

	}
}

function CommandTableDrop(tableName)
{
	this.tableName = tableName;
}
{
	CommandTableDrop.parse = function(tokens)
	{
		var returnValue;

		var tableName = tokens[2];
		var table = databaseCurrent.tables[tableName];
		if (table == null)
		{
			returnValue = new CommandError
			(
				"No table exists named '" + tableName + "'."
			);
		}
		else
		{
			returnValue = new CommandTableDrop();
		}

		return returnValue;
	}

	CommandTableDrop.prototype.execute = function()
	{
		var server = Globals.Instance.server;
		var database = server.databaseCurrent;
		var tables = database.tables;
		var table = tables[this.tableName];
		database.tableRemove(table);	
		return Command.titleAndMessageToTable("drop table", this.tableName);
	}
}

function CommandTableInsert(tableName, columnNameValuePairs)
{
	this.tableName = tableName;
	this.columnNameValuePairs = columnNameValuePairs;
}
{
	CommandTableInsert.parse = function(tokens)
	{
		var returnValue;

		var token1 = tokens[1];
		if (token1 == "into")
		{
			var databaseCurrent = Globals.Instance.server.databaseCurrent;
			if (databaseCurrent == null)
			{
				var errorMessage = 
					"No database currently selected."
					+ "  Try the 'use database' command first.";

				returnValue = new CommandError(errorMessage);				
			}
			else
			{
				var tableName = tokens[2];
	
				var table = databaseCurrent.tables[tableName];
				if (table == null)
				{
					returnValue = new CommandError
					(
						"No table exists named '" 
						+ tableName
						+ "'."
					);
				}
				else
				{
					var columnDefns = table.defn.columnDefns;

					var columnNameValuePairs = [];
				
					var token3 = tokens[3];
	
					if (token3 == "select")
					{
						for (var t = 4; t < tokens.length; t++)
						{
							var columnIndex = t - 4;
							var columnDefn = columnDefns[columnIndex];
							var columnName = columnDefn.name;
							var columnValue = tokens[t].replace(",", "");
							var columnNameValuePair = 
							[
								columnName,
								columnValue
							];
							columnNameValuePairs.push
							(
								columnNameValuePair
							);
						}

						returnValue = new CommandTableInsert
						(
							tableName, columnNameValuePairs
						);
					}
					else
					{
						returnValue = new CommandError
						(
							"Expected 'select' after 'insert into "
							+ tableName
							+ "'."
						);
					}
				}
			}
		}
		else
		{
			returnValue = new CommandError
			(
				"Unrecognized command: " + token0 + " " + token1
			);
		}

		return returnValue;
	}

	CommandTableInsert.prototype.execute = function()
	{
		var server = Globals.Instance.server;
		var database = server.databaseCurrent;
		var table = database.tables[this.tableName];
		var tableDefn = table.defn;
		var columnDefns = tableDefn.columnDefns;

		var columnValuesInOrder = [];

		for (var i = 0; i < this.columnNameValuePairs.length; i++)
		{
			var columnNameValuePair = this.columnNameValuePairs[i];
			var columnName = columnNameValuePair[0];
			var columnValue = columnNameValuePair[1];
			var columnDefn = columnDefns[columnName];
			if (columnDefn == null)
			{
				return new CommandError("error").execute();
			}
			else 
			{
				var columnIndex = columnDefn.indexWithinTable;
				columnValuesInOrder[columnIndex] = columnValue;
			}
		}

		var row = new Row(columnValuesInOrder);

		table.rows.push(row);
		
		return Command.titleAndMessageToTable
		(
			"insert into " + this.tableName, 
			"Row inserted."
		);
	}
}

function CommandTableSelect(tableReference, columnReferences, conditionExpression)
{
	this.tableReference = tableReference;
	this.columnReferences = columnReferences;
	this.conditionExpression = conditionExpression;
}
{
	CommandTableSelect.parse = function(tokens)
	{
		var returnValue;

		var databaseCurrent = Globals.Instance.server.databaseCurrent;
		if (databaseCurrent == null)
		{
			var errorMessage = 
				"No database currently selected."
				+ "  Try the 'use database' command first.";
			return new CommandError(errorMessage);				
		}

		var columnReferences = [];
	
		var t = 1;
		var token = tokens[t];

		while (token != "from")
		{
			var columnName = token;
			var columnReference = new ColumnReference(null, columnName);
			columnReferences.push(columnReference);
		
			t++;
			if (t >= tokens.length)
			{
				var errorMessage = "Expecting 'from'.";
				return new CommandError(errorMessage);
			}
			token = tokens[t];			
		}

		t++;
		var tableName = tokens[t].replace(",", "");
		var tableReference = new TableReference(tableName, null);

		var table = databaseCurrent.tables[tableName];
		if (table == null)
		{
			return new CommandError("No table exists named: " + tableName);
		}
		else
		{
			var columnDefns = table.defn.columnDefns;

			for (var i = 0; i < columnReferences.length; i++)
			{
				var columnReference = columnReferences[i];
				var columnName = columnReference.columnName;
				if (columnName != "*")
				{
					var columnDefn = columnDefns[columnName];
					if (columnDefn == null)
					{
						var errorMessage = 
							"No column named '" 
							+ columnName 
							+ "' exists on table.";

						returnValue = new CommandError(errorMessage);
					}
				}
			}
		}

		t++;

		var conditionExpression = null;

		if (t < tokens.length)
		{
			var where = tokens[t];
			if (where != "where")
			{
				return new CommandError("Expected 'where'.");
			}
			t++;

			conditionExpression = CommandTableSelect.parse_Comparison
			(
				tokens[t + 1],
				[
					tokens[t],
					tokens[t + 2]
				]
			);
			t += 3;

			while (t < tokens.length)
			{	
				var booleanOperator = tokens[t];
				t++;

				var operationInner = CommandTableSelect.parse_Comparison
				(
					tokens[t + 1],
					[
						tokens[t],
						tokens[t + 2]
					]
				);
				t += 3;

				conditionExpression = new Expression
				(
					booleanOperator,
					[
						conditionExpression,
						operationInner
					]
				);

			}
		}

		var returnValue = new CommandTableSelect
		(
			tableReference, columnReferences, conditionExpression
		);

		return returnValue;
	}

	CommandTableSelect.parse_Comparison = function(operatorComparator, operandsAsStrings)
	{
		var operandsAsExpressions = [];

		for (var i = 0; i < 2; i++)
		{
			var operandAsString = operandsAsStrings[i];

			var operandType;
			
			if (operandAsString[0].isLetter() == true)
			{
				operandType = "ColumnReference";
			}
			else
			{
				operandType = "Literal";
			}

			var operandAsExpression = new Expression
			(
				operandType, [ operandAsString ]
			);

			operandsAsExpressions.push(operandAsExpression);
		}

		var returnValue = new Expression
		(
			operatorComparator, 
			operandsAsExpressions
		)

		return returnValue;
	}

	// instance methods

	CommandTableSelect.prototype.execute = function()
	{
		var database = Globals.Instance.server.databaseCurrent;

		if (database == null)
		{
			return new CommandError("No database currently selected.");
		}
		
		var tableName = this.tableReference.tableName;
		var tableToSelectFrom = database.tables[tableName];

		if (tableToSelectFrom == null)
		{
			return new CommandError("No table exists named '" + tableName + "'.");
		}

		var columnDefnsToSelectFrom = tableToSelectFrom.defn.columnDefns;
		var columnDefnsSelected = [];

		for (var i = 0; i < this.columnReferences.length; i++)
		{
			var columnReference = this.columnReferences[i];
			var columnName = columnReference.columnName;
			if (columnName == "*")
			{
				columnDefnsSelected = columnDefnsSelected.concat
				(
					columnDefnsToSelectFrom
				);
			}
			else
			{
				var columnDefn = columnDefnsToSelectFrom[columnName];
				columnDefnsSelected.push(columnDefn);
			}
		}

		var rowsToSelectFrom = tableToSelectFrom.rows;
		var rowsSelected = [];

		for (var i = 0; i < rowsToSelectFrom.length; i++)
		{
			var rowToSelect = rowsToSelectFrom[i];

			var doesRowSatisfyConditions;

			if (this.conditionExpression == null)
			{
				doesRowSatisfyConditions = true;
			}
			else
			{
				doesRowSatisfyConditions = 
					this.conditionExpression.evaluate
					(
						tableToSelectFrom,
						rowToSelect
					);
			}

			if (doesRowSatisfyConditions == true)
			{
				var columnValuesSelectedForRow = [];
	
				for (var c = 0; c < columnDefnsSelected.length; c++)
				{
					var columnDefn = columnDefnsSelected[c];
					var columnIndex = columnDefn.indexWithinTable;
					var columnValue = rowToSelect.columnValues[columnIndex];
					columnValuesSelectedForRow.push(columnValue);
				}
	
				var rowSelected = new Row(columnValuesSelectedForRow);	
				rowsSelected.push(rowSelected)
			}
		}

		var tableJoinedDefn = new TableDefn(columnDefnsSelected);

		var commandAsString = this.toString();

		var tableJoined = new Table
		(
			commandAsString,
			tableJoinedDefn,
			rowsSelected
		);

		return tableJoined;
	}	

	CommandTableSelect.prototype.toString = function()
	{
		var returnValue = 
			"select " 
			+ this.columnReferences.join(",")
			+ " from " 
			+ this.tableReference.tableName;

		if (this.conditionExpression != null)
		{
			returnValue += " where...";
		}

		return returnValue;
	}
}

function CommandTableUpdate(tableName, columnNameValuePairs)
{
	this.tableName = tableName;
	this.columnNameValuePairs = columnNameValuePairs;
	// todo - where
}
{
	CommandTableUpdate.parse = function(tokens)
	{
		var returnValue;

		var databaseCurrent = Globals.Instance.server.databaseCurrent;

		if (databaseCurrent == null)
		{
			var errorMessage = 
				"No database currently selected."
				+ "  Try the 'use database' command first.";

			returnValue = new CommandError(errorMessage);				
		}
		else
		{
			var tableName = tokens[1];
			var table = databaseCurrent.tables[tableName];
			if (table == null)
			{
				returnValue = new CommandError("No table exists named '" + tableName + "'.");
			}
			else
			{
				returnValue = new CommandTableUpdate(tableName);
			}
		}

		return returnValue;
	}

	CommandTableUpdate.prototype.execute = function()
	{
		return Command.titleAndMessageToTable("update table", "Not yet implemented.");
	}
}

function Database(name, tables)
{
	this.name = name;
	this.tables = tables.addLookups("name");
}
{
	Database.prototype.tableAdd = function(table)
	{
		this.tables.push(table);	
		this.tables[table.name] = table;
	}

	Database.prototype.tableRemove = function(table)
	{
		this.tables.remove(table);	
		delete this.tables[table.name];
	}
}

function Datatype(name)
{
	this.name = name;
}
{
	Datatype.Instances = new Datatype_Instances();

	function Datatype_Instances()
	{
		this.Bit = new Datatype("bit");
		this.Int = new Datatype("int");
		this.Varchar = new Datatype("varchar");

		this._All = 
		[
			this.Bit,
			this.Int,
			this.Varchar
		]

		this._All.addLookups("name");
	}
}

function Expression(operator, operands)
{
	this.operator = operator;
	this.operands = operands;	
}
{
	Expression.prototype.evaluate = function(table, row)
	{
		var returnValue;

		var operator = this.operator;

		if (this.operator == "Literal")
		{
			returnValue = this.operands[0];
		}
		else if (this.operator == "ColumnReference")
		{
			var columnName = this.operands[0];
			var columnDefn = table.defn.columnDefns[columnName];
			var columnIndex = columnDefn.indexWithinTable;
			returnValue = row.columnValues[columnIndex];
		}
		else 
		{
			var operand0 = this.operands[0].evaluate(table, row);
			var operand1 = this.operands[1].evaluate(table, row);
		
			if (this.operator == "and")
			{
				returnValue = (operand0 && operand1);
			}	
			else if (this.operator == "or")	
			{
				returnValue = (operand0 || operand1);
			}
			else if (this.operator == "=")	
			{
				returnValue = (operand0 == operand1);
			}
			else if (this.operator == "<>")	
			{
				returnValue = (operand0 != operand1);
			}
			else if (this.operator == "<")	
			{
				returnValue = (operand0 < operand1);
			}
			else if (this.operator == ">")	
			{
				returnValue = (operand0 > operand1);
			}
			else if (this.operator == "<=")	
			{
				returnValue = (operand0 <= operand1);
			}
			else if (this.operator == ">=")	
			{
				returnValue = (operand0 >= operand1);
			}
		}

		return returnValue;
	}
}

function Globals()
{}
{
	Globals.Instance = new Globals();

	Globals.prototype.initialize = function(server, managementInterface)
	{
		this.server = server;
		this.managementInterface = managementInterface;
	}
}

function ManagementInterface()
{
	// todo
}
{
	ManagementInterface.prototype.executeCommands = function()
	{		
		var textareaCommand = document.getElementById("textareaCommand");
		var commandsAsString = textareaCommand.value;
		
		var commandsAsLines = commandsAsString.split("\n");
		var commentStartCode = "--";
		for (var i = 0; i < commandsAsLines.length; i++)
		{
			var line = commandsAsLines[i];
			var indexOfCommentStartCode = line.indexOf(commentStartCode);
			if (indexOfCommentStartCode >= 0)
			{
				line = line.substr(0, indexOfCommentStartCode);
			}
			commandsAsLines[i] = line;
		}

		commandsAsString = commandsAsLines.join("\n");

		var commandsAsStrings = commandsAsString.split(";");
		var resultsAsStrings = [];

		for (var i = 0; i < commandsAsStrings.length; i++)
		{
			var commandAsString = commandsAsStrings[i].trim();
			if (commandAsString.length > 0)
			{
				var command = Command.parse(commandAsString);
				var resultAsTable = command.execute();
				var resultAsString = resultAsTable.toString();
				resultsAsStrings.push(resultAsString);
			}
		}

		var textareaResults = document.getElementById("textareaResults");
		textareaResults.value = resultsAsStrings.join("\n");
	}
}
		
function Row(columnValues)
{
	this.columnValues = columnValues;
}

function Server(name, databases)
{
	this.name = name;
	this.databases = databases.addLookups("name");
	this.databaseCurrent = databases[0];
}
{
	Server.prototype.databaseAdd = function(database)
	{
		this.databases.push(database);
		this.databases[database.name] = database;
	}
}

function Table(name, defn, rows)
{
	this.name = name;
	this.defn = defn;
	this.rows = rows;
}
{
	Table.prototype.toString = function()
	{
		var newline = "\n";
		var space = " ";

		var columnDefns = this.defn.columnDefns;

		var columnWidths = [];

		for (var i = 0; i < columnDefns.length; i++)
		{
			var columnDefn = columnDefns[i];
			var columnName = columnDefn.name;
			var columnWidth = columnName.length;
			columnWidths.push(columnWidth);
		}

		for (var i = 0; i < this.rows.length; i++)
		{
			var row = this.rows[i];
			var columnValues = row.columnValues;
			for (var j = 0; j < columnValues.length; j++)
			{
				var columnValue = columnValues[j];

				var columnWidthPrev = columnWidths[j];
				if (columnValue.length >= columnWidthPrev)
				{
					columnWidths[j] = columnValue.length;
				}

			}
		}

		var columnHeadersAsString = "";
		var columnSeparators = "";

		for (var i = 0; i < columnDefns.length; i++)
		{
			var columnDefn = columnDefns[i];
			var columnName = columnDefn.name;
			var columnWidth = columnWidths[i];
			var columnNamePadded = columnName.padRight(columnWidth, space);
			columnHeadersAsString += columnName + space;
			var columnSeparator = 
				"".padRight(columnName.length, "-")
				+ space;
			columnSeparators += columnSeparator;				
		}

		var rowsAsString = "";

		for (var i = 0; i < this.rows.length; i++)
		{
			var row = this.rows[i];
			var columnValues = row.columnValues;
			for (var j = 0; j < columnValues.length; j++)
			{
				var columnValue = columnValues[j];
				var columnWidth = columnWidths[j];
				var columnValuePadded = columnValue.padRight
				(
					columnWidth, space
				);
				rowsAsString += columnValuePadded + space;
			}

			rowsAsString += newline;
		}

		var nameSeparator = "".padRight(this.name.length, "=");

		var returnValue = 
			this.name + newline 
			+ nameSeparator + newline
			+ columnHeadersAsString + newline
			+ columnSeparators + newline
			+ rowsAsString;
		
		return returnValue;
	}
}

function TableDefn(columnDefns)
{
	this.columnDefns = columnDefns;

	for (var c = 0; c < columnDefns.length; c++)
	{
		var columnDefn = columnDefns[c];
		columnDefn.indexWithinTable = c;
	}

	this.columnDefns.addLookups("name");
}

function TableReference(tableName, tableAlias)
{
	this.tableName = tableName;
	this.tableAlias = tableAlias;
}

// run

main();

</script>
</body>
</html>

Advertisements
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s