Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
cs122-19wi
nanodb-base
Commits
eb289261
Commit
eb289261
authored
6 years ago
by
Donald H. (Donnie) Pinkston, III
Browse files
Options
Download
Email Patches
Plain Diff
Initial commit of NanoDB Wi-2019
parents
master
No related merge requests found
Changes
259
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
src/main/antlr4/imports/Keywords.g4
+130
-0
src/main/antlr4/imports/Keywords.g4
src/main/antlr4/imports/Keywords.g4.in
+130
-0
src/main/antlr4/imports/Keywords.g4.in
src/main/antlr4/imports/Lexer.g4
+37
-0
src/main/antlr4/imports/Lexer.g4
src/main/antlr4/imports/make_ci.py
+45
-0
src/main/antlr4/imports/make_ci.py
src/main/java/edu/caltech/nanodb/client/ExclusiveClient.java
+88
-0
src/main/java/edu/caltech/nanodb/client/ExclusiveClient.java
src/main/java/edu/caltech/nanodb/client/InteractiveClient.java
+176
-0
...ain/java/edu/caltech/nanodb/client/InteractiveClient.java
src/main/java/edu/caltech/nanodb/client/SharedClientState.java
+189
-0
...ain/java/edu/caltech/nanodb/client/SharedClientState.java
src/main/java/edu/caltech/nanodb/client/SharedServerClient.java
+215
-0
...in/java/edu/caltech/nanodb/client/SharedServerClient.java
src/main/java/edu/caltech/nanodb/client/package.html
+9
-0
src/main/java/edu/caltech/nanodb/client/package.html
src/main/java/edu/caltech/nanodb/commands/AnalyzeCommand.java
+145
-0
...main/java/edu/caltech/nanodb/commands/AnalyzeCommand.java
src/main/java/edu/caltech/nanodb/commands/BeginTransactionCommand.java
+32
-0
.../edu/caltech/nanodb/commands/BeginTransactionCommand.java
src/main/java/edu/caltech/nanodb/commands/Command.java
+78
-0
src/main/java/edu/caltech/nanodb/commands/Command.java
src/main/java/edu/caltech/nanodb/commands/CommandProperties.java
+74
-0
...n/java/edu/caltech/nanodb/commands/CommandProperties.java
src/main/java/edu/caltech/nanodb/commands/CommitTransactionCommand.java
+30
-0
...edu/caltech/nanodb/commands/CommitTransactionCommand.java
src/main/java/edu/caltech/nanodb/commands/ConstraintDecl.java
+330
-0
...main/java/edu/caltech/nanodb/commands/ConstraintDecl.java
src/main/java/edu/caltech/nanodb/commands/CrashCommand.java
+91
-0
src/main/java/edu/caltech/nanodb/commands/CrashCommand.java
src/main/java/edu/caltech/nanodb/commands/CreateIndexCommand.java
+211
-0
.../java/edu/caltech/nanodb/commands/CreateIndexCommand.java
src/main/java/edu/caltech/nanodb/commands/CreateTableCommand.java
+448
-0
.../java/edu/caltech/nanodb/commands/CreateTableCommand.java
src/main/java/edu/caltech/nanodb/commands/CreateViewCommand.java
+37
-0
...n/java/edu/caltech/nanodb/commands/CreateViewCommand.java
src/main/java/edu/caltech/nanodb/commands/DDLUtils.java
+104
-0
src/main/java/edu/caltech/nanodb/commands/DDLUtils.java
with
2599 additions
and
0 deletions
+2599
-0
Too many changes to show.
To preserve performance only
259 of 259+
files are displayed.
Plain diff
Email patch
src/main/antlr4/imports/Keywords.g4
0 → 100644
View file @
eb289261
grammar Keywords;
// These are the keywords that NanoDB recognizes. As usual, the set definitely
// overlaps standard SQL, and it also includes other non-standard commands.
ADD : [Aa][Dd][Dd] ;
ALL : [Aa][Ll][Ll] ;
ALTER : [Aa][Ll][Tt][Ee][Rr] ;
ANALYZE : [Aa][Nn][Aa][Ll][Yy][Zz][Ee] ;
AND : [Aa][Nn][Dd] ;
ANY : [Aa][Nn][Yy] ;
AS : [Aa][Ss] ;
ASC : [Aa][Ss][Cc] ;
ASCENDING : [Aa][Ss][Cc][Ee][Nn][Dd][Ii][Nn][Gg] ;
BEGIN : [Bb][Ee][Gg][Ii][Nn] ;
BETWEEN : [Bb][Ee][Tt][Ww][Ee][Ee][Nn] ;
BY : [Bb][Yy] ;
CASCADE : [Cc][Aa][Ss][Cc][Aa][Dd][Ee] ;
CHECK : [Cc][Hh][Ee][Cc][Kk] ;
COLUMN : [Cc][Oo][Ll][Uu][Mm][Nn] ;
COMMIT : [Cc][Oo][Mm][Mm][Ii][Tt] ;
CONSTRAINT : [Cc][Oo][Nn][Ss][Tt][Rr][Aa][Ii][Nn][Tt] ;
CRASH : [Cc][Rr][Aa][Ss][Hh] ;
CREATE : [Cc][Rr][Ee][Aa][Tt][Ee] ;
CROSS : [Cc][Rr][Oo][Ss][Ss] ;
DEFAULT : [Dd][Ee][Ff][Aa][Uu][Ll][Tt] ;
DELETE : [Dd][Ee][Ll][Ee][Tt][Ee] ;
DESC : [Dd][Ee][Ss][Cc] ;
DESCENDING : [Dd][Ee][Ss][Cc][Ee][Nn][Dd][Ii][Nn][Gg] ;
DISTINCT : [Dd][Ii][Ss][Tt][Ii][Nn][Cc][Tt] ;
DROP : [Dd][Rr][Oo][Pp] ;
DUMP : [Dd][Uu][Mm][Pp] ;
EXCEPT : [Ee][Xx][Cc][Ee][Pp][Tt] ;
EXISTS : [Ee][Xx][Ii][Ss][Tt][Ss] ;
EXIT : [Ee][Xx][Ii][Tt] ;
EXPLAIN : [Ee][Xx][Pp][Ll][Aa][Ii][Nn] ;
FALSE : [Ff][Aa][Ll][Ss][Ee] ;
FILE : [Ff][Ii][Ll][Ee] ;
FLUSH : [Ff][Ll][Uu][Ss][Hh] ;
FOREIGN : [Ff][Oo][Rr][Ee][Ii][Gg][Nn] ;
FORMAT : [Ff][Oo][Rr][Mm][Aa][Tt] ;
FROM : [Ff][Rr][Oo][Mm] ;
FULL : [Ff][Uu][Ll][Ll] ;
GROUP : [Gg][Rr][Oo][Uu][Pp] ;
HAVING : [Hh][Aa][Vv][Ii][Nn][Gg] ;
IF : [Ii][Ff] ;
IN : [Ii][Nn] ;
INDEX : [Ii][Nn][Dd][Ee][Xx] ;
INNER : [Ii][Nn][Nn][Ee][Rr] ;
INSERT : [Ii][Nn][Ss][Ee][Rr][Tt] ;
INTERSECT : [Ii][Nn][Tt][Ee][Rr][Ss][Ee][Cc][Tt] ;
INTERVAL : [Ii][Nn][Tt][Ee][Rr][Vv][Aa][Ll] ;
INTO : [Ii][Nn][Tt][Oo] ;
IS : [Ii][Ss] ;
JOIN : [Jj][Oo][Ii][Nn] ;
KEY : [Kk][Ee][Yy] ;
LEFT : [Ll][Ee][Ff][Tt] ;
LIKE : [Ll][Ii][Kk][Ee] ;
LIMIT : [Ll][Ii][Mm][Ii][Tt] ;
MINUS : [Mm][Ii][Nn][Uu][Ss] ;
NATURAL : [Nn][Aa][Tt][Uu][Rr][Aa][Ll] ;
NOT : [Nn][Oo][Tt] ;
NULL : [Nn][Uu][Ll][Ll] ;
OFFSET : [Oo][Ff][Ff][Ss][Ee][Tt] ;
ON : [Oo][Nn] ;
OPTIMIZE : [Oo][Pp][Tt][Ii][Mm][Ii][Zz][Ee] ;
OR : [Oo][Rr] ;
ORDER : [Oo][Rr][Dd][Ee][Rr] ;
OUTER : [Oo][Uu][Tt][Ee][Rr] ;
PRIMARY : [Pp][Rr][Ii][Mm][Aa][Rr][Yy] ;
PROPERTIES : [Pp][Rr][Oo][Pp][Ee][Rr][Tt][Ii][Ee][Ss] ;
PROPERTY : [Pp][Rr][Oo][Pp][Ee][Rr][Tt][Yy] ;
QUIT : [Qq][Uu][Ii][Tt] ;
RECURSIVE : [Rr][Ee][Cc][Uu][Rr][Ss][Ii][Vv][Ee] ;
REFERENCES : [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee][Ss] ;
RENAME : [Rr][Ee][Nn][Aa][Mm][Ee] ;
RESTRICT : [Rr][Ee][Ss][Tt][Rr][Ii][Cc][Tt] ;
RIGHT : [Rr][Ii][Gg][Hh][Tt] ;
ROLLBACK : [Rr][Oo][Ll][Ll][Bb][Aa][Cc][Kk] ;
SELECT : [Ss][Ee][Ll][Ee][Cc][Tt] ;
SET : [Ss][Ee][Tt] ;
SHOW : [Ss][Hh][Oo][Ww] ;
SIMILAR : [Ss][Ii][Mm][Ii][Ll][Aa][Rr] ;
SOME : [Ss][Oo][Mm][Ee] ;
START : [Ss][Tt][Aa][Rr][Tt] ;
STATS : [Ss][Tt][Aa][Tt][Ss] ;
TABLE : [Tt][Aa][Bb][Ll][Ee] ;
TABLES : [Tt][Aa][Bb][Ll][Ee][Ss] ;
TEMPORARY : [Tt][Ee][Mm][Pp][Oo][Rr][Aa][Rr][Yy] ;
TO : [Tt][Oo] ;
TRANSACTION : [Tt][Rr][Aa][Nn][Ss][Aa][Cc][Tt][Ii][Oo][Nn] ;
TRUE : [Tt][Rr][Uu][Ee] ;
TYPE : [Tt][Yy][Pp][Ee] ;
UNION : [Uu][Nn][Ii][Oo][Nn] ;
UNIQUE : [Uu][Nn][Ii][Qq][Uu][Ee] ;
UNKNOWN : [Uu][Nn][Kk][Nn][Oo][Ww][Nn] ;
UPDATE : [Uu][Pp][Dd][Aa][Tt][Ee] ;
USING : [Uu][Ss][Ii][Nn][Gg] ;
VALUES : [Vv][Aa][Ll][Uu][Ee][Ss] ;
VERBOSE : [Vv][Ee][Rr][Bb][Oo][Ss][Ee] ;
VERIFY : [Vv][Ee][Rr][Ii][Ff][Yy] ;
VIEW : [Vv][Ii][Ee][Ww] ;
WHERE : [Ww][Hh][Ee][Rr][Ee] ;
WITH : [Ww][Ii][Tt][Hh] ;
WORK : [Ww][Oo][Rr][Kk] ;
// These tokens are for type-recognition. A number of these types have
// additional syntax for specifying length or precision, which is why we have
// parser rules for them. The type-system is not extensible by database users.
// Note also that not all of these types are supported by NanoDB; they are
// primarily included to reserve the keywords.
TYPE_BIGINT : [Bb][Ii][Gg][Ii][Nn][Tt] ;
TYPE_BLOB : [Bb][Ll][Oo][Bb] ;
TYPE_CHAR : [Cc][Hh][Aa][Rr] ; // char(length)
TYPE_CHARACTER : [Cc][Hh][Aa][Rr][Aa][Cc][Tt][Ee][Rr] ; // character(length)
TYPE_DATE : [Dd][Aa][Tt][Ee] ;
TYPE_DATETIME : [Dd][Aa][Tt][Ee][Tt][Ii][Mm][Ee] ;
TYPE_DECIMAL : [Dd][Ee][Cc][Ii][Mm][Aa][Ll] ; // decimal, decimal(prec), decimal(prec, scale)
TYPE_FLOAT : [Ff][Ll][Oo][Aa][Tt] ; // float, float(prec)
TYPE_DOUBLE : [Dd][Oo][Uu][Bb][Ll][Ee] ; // double
TYPE_INT : [Ii][Nn][Tt] ;
TYPE_INTEGER : [Ii][Nn][Tt][Ee][Gg][Ee][Rr] ;
TYPE_NUMERIC : [Nn][Uu][Mm][Ee][Rr][Ii][Cc] ; // numeric, numeric(prec), numeric(prec, scale)
TYPE_SMALLINT : [Ss][Mm][Aa][Ll][Ll][Ii][Nn][Tt] ;
TYPE_TEXT : [Tt][Ee][Xx][Tt] ;
TYPE_TIME : [Tt][Ii][Mm][Ee] ;
TYPE_TIMESTAMP : [Tt][Ii][Mm][Ee][Ss][Tt][Aa][Mm][Pp] ;
TYPE_TINYINT : [Tt][Ii][Nn][Yy][Ii][Nn][Tt] ;
TYPE_VARCHAR : [Vv][Aa][Rr][Cc][Hh][Aa][Rr] ; // varchar(length)
TYPE_VARYING : [Vv][Aa][Rr][Yy][Ii][Nn][Gg] ; // character varying(length)
This diff is collapsed.
Click to expand it.
src/main/antlr4/imports/Keywords.g4.in
0 → 100755
View file @
eb289261
grammar Keywords;
// These are the keywords that NanoDB recognizes. As usual, the set definitely
// overlaps standard SQL, and it also includes other non-standard commands.
ADD : 'add' ;
ALL : 'all' ;
ALTER : 'alter' ;
ANALYZE : 'analyze' ;
AND : 'and' ;
ANY : 'any' ;
AS : 'as' ;
ASC : 'asc' ;
ASCENDING : 'ascending' ;
BEGIN : 'begin' ;
BETWEEN : 'between' ;
BY : 'by' ;
CASCADE : 'cascade' ;
CHECK : 'check' ;
COLUMN : 'column' ;
COMMIT : 'commit' ;
CONSTRAINT : 'constraint' ;
CRASH : 'crash' ;
CREATE : 'create' ;
CROSS : 'cross' ;
DEFAULT : 'default' ;
DELETE : 'delete' ;
DESC : 'desc' ;
DESCENDING : 'descending' ;
DISTINCT : 'distinct' ;
DROP : 'drop' ;
DUMP : 'dump' ;
EXCEPT : 'except' ;
EXISTS : 'exists' ;
EXIT : 'exit' ;
EXPLAIN : 'explain' ;
FALSE : 'false' ;
FILE : 'file' ;
FLUSH : 'flush' ;
FOREIGN : 'foreign' ;
FORMAT : 'format' ;
FROM : 'from' ;
FULL : 'full' ;
GROUP : 'group' ;
HAVING : 'having' ;
IF : 'if' ;
IN : 'in' ;
INDEX : 'index' ;
INNER : 'inner' ;
INSERT : 'insert' ;
INTERSECT : 'intersect' ;
INTERVAL : 'interval' ;
INTO : 'into' ;
IS : 'is' ;
JOIN : 'join' ;
KEY : 'key' ;
LEFT : 'left' ;
LIKE : 'like' ;
LIMIT : 'limit' ;
MINUS : 'minus' ;
NATURAL : 'natural' ;
NOT : 'not' ;
NULL : 'null' ;
OFFSET : 'offset' ;
ON : 'on' ;
OPTIMIZE : 'optimize' ;
OR : 'or' ;
ORDER : 'order' ;
OUTER : 'outer' ;
PRIMARY : 'primary' ;
PROPERTIES : 'properties' ;
PROPERTY : 'property' ;
QUIT : 'quit' ;
RECURSIVE : 'recursive' ;
REFERENCES : 'references' ;
RENAME : 'rename' ;
RESTRICT : 'restrict' ;
RIGHT : 'right' ;
ROLLBACK : 'rollback' ;
SELECT : 'select' ;
SET : 'set' ;
SHOW : 'show' ;
SIMILAR : 'similar' ;
SOME : 'some' ;
START : 'start' ;
STATS : 'stats' ;
TABLE : 'table' ;
TABLES : 'tables' ;
TEMPORARY : 'temporary' ;
TO : 'to' ;
TRANSACTION : 'transaction' ;
TRUE : 'true' ;
TYPE : 'type' ;
UNION : 'union' ;
UNIQUE : 'unique' ;
UNKNOWN : 'unknown' ;
UPDATE : 'update' ;
USING : 'using' ;
VALUES : 'values' ;
VERBOSE : 'verbose' ;
VERIFY : 'verify' ;
VIEW : 'view' ;
WHERE : 'where' ;
WITH : 'with' ;
WORK : 'work' ;
// These tokens are for type-recognition. A number of these types have
// additional syntax for specifying length or precision, which is why we have
// parser rules for them. The type-system is not extensible by database users.
// Note also that not all of these types are supported by NanoDB; they are
// primarily included to reserve the keywords.
TYPE_BIGINT : 'bigint' ;
TYPE_BLOB : 'blob' ;
TYPE_CHAR : 'char' ; // char(length)
TYPE_CHARACTER : 'character' ; // character(length)
TYPE_DATE : 'date' ;
TYPE_DATETIME : 'datetime' ;
TYPE_DECIMAL : 'decimal' ; // decimal, decimal(prec), decimal(prec, scale)
TYPE_FLOAT : 'float' ; // float, float(prec)
TYPE_DOUBLE : 'double' ; // double
TYPE_INT : 'int' ;
TYPE_INTEGER : 'integer' ;
TYPE_NUMERIC : 'numeric' ; // numeric, numeric(prec), numeric(prec, scale)
TYPE_SMALLINT : 'smallint' ;
TYPE_TEXT : 'text' ;
TYPE_TIME : 'time' ;
TYPE_TIMESTAMP : 'timestamp' ;
TYPE_TINYINT : 'tinyint' ;
TYPE_VARCHAR : 'varchar' ; // varchar(length)
TYPE_VARYING : 'varying' ; // character varying(length)
This diff is collapsed.
Click to expand it.
src/main/antlr4/imports/Lexer.g4
0 → 100644
View file @
eb289261
grammar Literals;
//============================================================================
// Literals
// We keep literals pretty simple in NanoDB. Values can be single-quoted
// strings, or integer values (converted to Integer/Long/BigInteger as
// necessary for precision), or decimal values (converted to BigDecimal,
// but also castable to float or double as necessary).
//
IDENT:
[A-Za-z_] [A-Za-z0-9_]* ;
INT_LITERAL:
[0-9]+ ;
DECIMAL_LITERAL:
[0-9]+ '.' [0-9]* | '.' [0-9]+ ;
STRING_LITERAL:
'\'' .*? '\'' ;
//============================================================================
// Stuff We Ignore
//
// Define whitespace rule, toss it out
WS:
[ \t\r\n]+ -> skip ;
// Toss out comments as well
COMMENT:
'/*' .*? '*/' -> skip ;
LINE_COMMENT:
'--' .*? '\n' -> skip ;
This diff is collapsed.
Click to expand it.
src/main/antlr4/imports/make_ci.py
0 → 100644
View file @
eb289261
import
sys
def
make_insensitive
(
filename
):
if
not
filename
.
endswith
(
'.g4.in'
):
raise
ValueError
(
'Unrecognized file-type for %s'
%
filename
)
# Chop off the ".in" part
out_filename
=
filename
[:
-
3
]
with
open
(
filename
)
as
inp
,
open
(
out_filename
,
'w'
)
as
out
:
while
True
:
line
=
inp
.
readline
()
if
line
==
''
:
break
i_comment
=
line
.
find
(
"//"
)
i_quote
=
line
.
find
(
"'"
)
if
i_quote
!=
-
1
and
(
i_comment
==
-
1
or
i_quote
<
i_comment
):
i_endquote
=
line
.
find
(
"'"
,
i_quote
+
1
)
if
i_endquote
!=
-
1
:
text
=
line
[
i_quote
+
1
:
i_endquote
]
text
=
text
.
upper
()
insens
=
[
'[%c%c]'
%
(
ch
,
ch
.
lower
())
for
ch
in
text
]
insens
=
''
.
join
(
insens
)
line
=
line
[:
i_quote
]
+
insens
+
line
[
i_endquote
+
1
:]
out
.
write
(
line
)
if
len
(
sys
.
argv
)
==
1
:
print
(
'usage: %s file1.g4.in [file2.g4.in ...]'
%
sys
.
argv
[
0
])
print
(
'
\t
Converts the ANTLRv4 keyword file(s) to be case-insensitive.'
)
print
(
'
\t
Requires at least one file to be specified on the command-line.'
)
sys
.
exit
(
1
)
for
filename
in
sys
.
argv
[
1
:]:
if
not
filename
.
endswith
(
'.g4.in'
):
print
(
'WARNING: Unrecognized file-type for %s, skipping.'
%
filename
)
continue
make_insensitive
(
filename
)
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/client/ExclusiveClient.java
0 → 100755
View file @
eb289261
package
edu.caltech.nanodb.client
;
import
java.io.IOException
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.commands.Command
;
import
edu.caltech.nanodb.server.CommandResult
;
import
edu.caltech.nanodb.server.NanoDBException
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
org.antlr.v4.runtime.misc.ParseCancellationException
;
/**
* <p>
* This class is used for starting the NanoDB database in "exclusive mode,"
* where only a single client interacts directly with the database system.
* In fact, the exclusive client embeds the {@link NanoDBServer} instance in
* the client object, since there is no need to interact with the server over
* a network socket.
* </p>
* <p>
* <b>Note that it is <u>wrong</u> to start multiple exclusive clients against
* the same data directory!!!</b> Exclusive-mode operation expects that only
* this server is interacting with the data files. For concurrent access from
* multiple clients, see the {@link SharedServerClient} class.
* </p>
*/
public
class
ExclusiveClient
extends
InteractiveClient
{
private
static
Logger
logger
=
LogManager
.
getLogger
(
ExclusiveClient
.
class
);
/** The server that this exclusive client is using. */
private
NanoDBServer
server
;
@Override
public
void
startup
()
{
// Start up the various database subsystems that require initialization.
server
=
new
NanoDBServer
();
try
{
server
.
startup
();
}
catch
(
NanoDBException
e
)
{
System
.
out
.
println
(
"DATABASE STARTUP FAILED:"
);
e
.
printStackTrace
(
System
.
out
);
System
.
exit
(
1
);
}
}
@Override
public
CommandResult
handleCommand
(
String
command
)
{
CommandResult
result
=
server
.
doCommand
(
command
,
false
);
if
(
result
.
failed
())
{
Exception
e
=
result
.
getFailure
();
if
(
e
instanceof
ParseCancellationException
)
{
System
.
out
.
println
(
"ERROR: Could not parse command"
);
}
else
{
System
.
out
.
println
(
"ERROR: "
+
e
.
getMessage
());
}
}
return
result
;
}
@Override
public
void
shutdown
()
{
// Shut down the various database subsystems that require cleanup.
if
(!
server
.
shutdown
())
System
.
out
.
println
(
"DATABASE SHUTDOWN FAILED."
);
}
public
static
void
main
(
String
args
[])
{
ExclusiveClient
client
=
new
ExclusiveClient
();
client
.
startup
();
client
.
mainloop
();
client
.
shutdown
();
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/client/InteractiveClient.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.client
;
import
java.io.BufferedReader
;
import
java.io.InputStreamReader
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.server.CommandResult
;
/**
* This abstract class implements the basic functionality necessary for
* providing an interactive SQL client.
*/
public
abstract
class
InteractiveClient
{
private
static
Logger
logger
=
LogManager
.
getLogger
(
InteractiveClient
.
class
);
/** A string constant specifying the "first-line" command-prompt. */
private
static
final
String
CMDPROMPT_FIRST
=
"CMD> "
;
/** A string constant specifying the "subsequent-lines" command-prompt. */
private
static
final
String
CMDPROMPT_NEXT
=
" > "
;
/** The buffer that accumulates each command's text. */
private
StringBuilder
enteredText
;
public
abstract
void
startup
()
throws
Exception
;
public
void
mainloop
()
{
// We don't use the console directly, since we can't read/write it
// if someone redirects a file onto the client's input-stream.
boolean
hasConsole
=
(
System
.
console
()
!=
null
);
if
(
hasConsole
)
{
System
.
out
.
println
(
"Welcome to NanoDB. Exit with EXIT or QUIT command.\n"
);
}
boolean
exiting
=
false
;
BufferedReader
bufReader
=
new
BufferedReader
(
new
InputStreamReader
(
System
.
in
));
while
(!
exiting
)
{
enteredText
=
new
StringBuilder
();
boolean
firstLine
=
true
;
getcmd:
while
(
true
)
{
try
{
if
(
hasConsole
)
{
if
(
firstLine
)
{
System
.
out
.
print
(
CMDPROMPT_FIRST
);
System
.
out
.
flush
();
firstLine
=
false
;
}
else
{
System
.
out
.
print
(
CMDPROMPT_NEXT
);
System
.
out
.
flush
();
}
}
String
line
=
bufReader
.
readLine
();
if
(
line
==
null
)
{
// Hit EOF.
exiting
=
true
;
break
;
}
enteredText
.
append
(
line
).
append
(
'\n'
);
// Process any commands in the entered text.
while
(
true
)
{
String
command
=
getCommandString
();
if
(
command
==
null
)
break
;
// No more complete commands.
// if (logger.isDebugEnabled())
// logger.debug("Command string:\n" + command);
CommandResult
result
=
handleCommand
(
command
);
if
(
result
.
isExit
())
{
exiting
=
true
;
break
getcmd
;
}
}
if
(
enteredText
.
length
()
==
0
)
firstLine
=
true
;
}
catch
(
Throwable
e
)
{
System
.
out
.
println
(
"Unexpected error: "
+
e
.
getClass
()
+
": "
+
e
.
getMessage
());
logger
.
error
(
"Unexpected error"
,
e
);
}
}
}
}
/**
* This helper method goes through the {@link #enteredText} buffer, trying
* to identify the extent of the next command string. This is done using
* semicolons (that are not enclosed with single or double quotes). If a
* command is identified, it is removed from the internal buffer and
* returned. If no complete command is identified, {@code null} is
* returned.
*
* @return the first semicolon-terminated command in the internal data
* buffer, or {@code null} if the buffer contains no complete
* commands.
*/
private
String
getCommandString
()
{
int
i
=
0
;
String
command
=
null
;
while
(
i
<
enteredText
.
length
())
{
char
ch
=
enteredText
.
charAt
(
i
);
if
(
ch
==
';'
)
{
// Found the end of the command. Extract the string, and
// make sure the semicolon is also included.
command
=
enteredText
.
substring
(
0
,
i
+
1
);
enteredText
.
delete
(
0
,
i
+
1
);
// Consume any leading whitespace at the start of the entered
// text.
while
(
enteredText
.
length
()
>
0
&&
Character
.
isWhitespace
(
enteredText
.
charAt
(
0
)))
{
enteredText
.
deleteCharAt
(
0
);
}
break
;
}
else
if
(
ch
==
'\''
||
ch
==
'"'
)
{
// Need to ignore all subsequent characters until we find
// the end of this quoted string.
i
++;
while
(
i
<
enteredText
.
length
()
&&
enteredText
.
charAt
(
i
)
!=
ch
)
{
i
++;
}
}
i
++;
// Go on to the next character.
}
return
command
;
}
/**
* Subclasses can implement this method to handle each command entered
* by the user. For example, a subclass may send the command over a
* socket to the server, wait for a response, then output the response
* to the console.
*
* @param command the command to handle.
*
* @return the command-result from executing the command
*/
public
abstract
CommandResult
handleCommand
(
String
command
);
/**
* Shut down the interactive client. The specific way the client
* interacts with the server dictates how this shutdown mechanism
* will work.
*
* @throws Exception if any error occurs during shutdown
*/
public
abstract
void
shutdown
()
throws
Exception
;
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/client/SharedClientState.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.client
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.ObjectInputStream
;
import
java.io.ObjectOutputStream
;
import
java.io.PrintStream
;
import
java.net.Socket
;
import
java.net.SocketException
;
import
java.nio.channels.ClosedByInterruptException
;
import
java.util.ArrayList
;
import
java.util.concurrent.Semaphore
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.expressions.TupleLiteral
;
import
edu.caltech.nanodb.queryeval.PrettyTuplePrinter
;
import
edu.caltech.nanodb.relations.Schema
;
import
edu.caltech.nanodb.relations.Tuple
;
import
edu.caltech.nanodb.server.CommandState
;
/**
* This class represents
*
*/
public
class
SharedClientState
{
private
static
Logger
logger
=
LogManager
.
getLogger
(
SharedClientState
.
class
);
private
Socket
socket
;
private
ObjectInputStream
objectInput
;
private
Receiver
receiver
;
/**
* This thread receives data from the server asynchronously, and prints
* out whatever it receives.
*/
private
Thread
receiverThread
;
private
PrintStream
out
;
private
boolean
printTuples
;
private
Semaphore
semCommandDone
;
private
Schema
schema
;
private
ArrayList
<
TupleLiteral
>
tuples
;
/**
* This stream is used to send objects (commands, specifically) to the
*/
private
ObjectOutputStream
objectOutput
;
private
class
Receiver
implements
Runnable
{
private
PrintStream
out
;
private
boolean
done
;
public
Receiver
(
PrintStream
out
)
{
this
.
out
=
out
;
}
public
void
run
()
{
PrettyTuplePrinter
tuplePrinter
=
null
;
done
=
false
;
while
(
true
)
{
try
{
Object
obj
=
objectInput
.
readObject
();
if
(
obj
instanceof
String
)
{
// Just print strings to the console
System
.
out
.
print
(
obj
);
}
else
if
(
obj
instanceof
Schema
)
{
tuplePrinter
=
new
PrettyTuplePrinter
(
out
);
tuplePrinter
.
setSchema
((
Schema
)
obj
);
}
else
if
(
obj
instanceof
Tuple
)
{
tuplePrinter
.
process
((
Tuple
)
obj
);
}
else
if
(
obj
instanceof
Throwable
)
{
Throwable
t
=
(
Throwable
)
obj
;
t
.
printStackTrace
(
System
.
out
);
}
else
if
(
obj
instanceof
CommandState
)
{
CommandState
state
=
(
CommandState
)
obj
;
if
(
state
==
CommandState
.
COMMAND_COMPLETED
)
{
if
(
tuplePrinter
!=
null
)
{
tuplePrinter
.
finish
();
tuplePrinter
=
null
;
}
// Signal that the command is completed.
semCommandDone
.
release
();
}
}
else
{
// TODO: Try to print...
System
.
out
.
println
(
obj
);
}
}
catch
(
EOFException
e
)
{
System
.
out
.
println
(
"Connection was closed by the server."
);
break
;
}
catch
(
SocketException
e
)
{
System
.
out
.
println
(
"Connection was closed by the server."
);
break
;
}
catch
(
ClosedByInterruptException
e
)
{
System
.
out
.
println
(
"Thread was interrupted during an IO operation."
);
break
;
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
"Exception occurred:"
);
e
.
printStackTrace
(
System
.
out
);
}
}
}
public
void
shutdown
()
{
done
=
true
;
}
}
public
SharedClientState
(
PrintStream
out
,
boolean
printTuples
)
{
this
.
out
=
out
;
this
.
printTuples
=
printTuples
;
}
public
void
connect
(
String
hostname
,
int
port
)
throws
IOException
{
// Try to establish a connection to the shared database server.
socket
=
new
Socket
(
hostname
,
port
);
objectOutput
=
new
ObjectOutputStream
(
socket
.
getOutputStream
());
objectInput
=
new
ObjectInputStream
(
socket
.
getInputStream
());
// A semaphore to synchronize the receiver with the code that
// dispatches commands, so that we don't return from dispatching a
// command until the server says the command is finished.
semCommandDone
=
new
Semaphore
(
0
);
// Start up the receiver thread that will print out whatever comes
// across the wire.
receiver
=
new
Receiver
(
System
.
out
);
receiverThread
=
new
Thread
(
receiver
);
receiverThread
.
start
();
}
public
void
doCommand
(
String
commandString
)
throws
Exception
{
schema
=
null
;
tuples
.
clear
();
// Send the command to the server!
objectOutput
.
writeObject
(
commandString
);
// Wait for the command to be completed.
semCommandDone
.
acquire
();
}
public
void
disconnect
()
throws
IOException
{
receiver
.
shutdown
();
receiverThread
.
interrupt
();
objectInput
.
close
();
objectOutput
.
close
();
socket
.
close
();
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/client/SharedServerClient.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.client
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.io.ObjectInputStream
;
import
java.io.ObjectOutputStream
;
import
java.io.PrintStream
;
import
java.net.Socket
;
import
java.net.SocketException
;
import
java.nio.channels.ClosedByInterruptException
;
import
java.util.concurrent.Semaphore
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.commands.Command
;
import
edu.caltech.nanodb.queryeval.PrettyTuplePrinter
;
import
edu.caltech.nanodb.relations.Schema
;
import
edu.caltech.nanodb.relations.Tuple
;
import
edu.caltech.nanodb.server.CommandResult
;
import
edu.caltech.nanodb.server.CommandState
;
import
edu.caltech.nanodb.server.SharedServer
;
/**
* This class implements a client the can connect to the NanoDB
* {@link edu.caltech.nanodb.server.SharedServer shared server} and
* send/receive commands and data.
*/
public
class
SharedServerClient
extends
InteractiveClient
{
private
static
Logger
logger
=
LogManager
.
getLogger
(
SharedServerClient
.
class
);
/** The socket used to communicate with the shared server. */
private
Socket
socket
;
/**
* This stream is used to receive objects (tuples, messages, etc.) from
* the server. */
private
ObjectInputStream
objectInput
;
/**
* This stream is used to send objects (commands, specifically) to the
* server.
*/
private
ObjectOutputStream
objectOutput
;
/**
* This object receives data from the server asynchronously,
* and prints out whatever it receives. It is wrapped by the
* {@link #receiverThread}.
*/
private
Receiver
receiver
;
/**
* This is the thread that the {@link #receiver} object runs within.
*/
private
Thread
receiverThread
;
/**
* This semaphore is used to coordinate when a command has been sent to
* the server, and when the server is finished sending results back to
* the client, so that another command cannot be sent until the current
* one is finished.
*/
private
Semaphore
semCommandDone
;
/**
* This helper class prints out the results that come back from the
* server. It is intended to run within a separate thread.
*/
private
class
Receiver
implements
Runnable
{
/** The print-stream to output server results on. */
private
PrintStream
out
;
/** A flag indicating when the receiver thread should shut down. */
private
boolean
done
;
public
Receiver
(
PrintStream
out
)
{
this
.
out
=
out
;
}
public
void
run
()
{
PrettyTuplePrinter
tuplePrinter
=
null
;
done
=
false
;
while
(
true
)
{
try
{
Object
obj
=
objectInput
.
readObject
();
if
(
obj
instanceof
String
)
{
// Just print strings to the console
System
.
out
.
print
(
obj
);
}
else
if
(
obj
instanceof
Schema
)
{
tuplePrinter
=
new
PrettyTuplePrinter
(
out
);
tuplePrinter
.
setSchema
((
Schema
)
obj
);
}
else
if
(
obj
instanceof
Tuple
)
{
tuplePrinter
.
process
((
Tuple
)
obj
);
}
else
if
(
obj
instanceof
Throwable
)
{
Throwable
t
=
(
Throwable
)
obj
;
t
.
printStackTrace
(
System
.
out
);
}
else
if
(
obj
instanceof
CommandResult
)
{
CommandResult
result
=
(
CommandResult
)
obj
;
if
(
result
.
isExit
())
done
=
true
;
}
else
if
(
obj
instanceof
CommandState
)
{
CommandState
state
=
(
CommandState
)
obj
;
if
(
state
==
CommandState
.
COMMAND_COMPLETED
)
{
if
(
tuplePrinter
!=
null
)
{
tuplePrinter
.
finish
();
tuplePrinter
=
null
;
}
// Signal that the command is completed.
semCommandDone
.
release
();
}
}
else
{
// TODO: Try to print whatever came across the wire.
System
.
out
.
println
(
obj
);
}
}
catch
(
EOFException
e
)
{
System
.
out
.
println
(
"Connection was closed by the server."
);
break
;
}
catch
(
SocketException
e
)
{
System
.
out
.
println
(
"Socket communication error."
);
break
;
}
catch
(
ClosedByInterruptException
e
)
{
System
.
out
.
println
(
"Thread was interrupted during an IO operation."
);
break
;
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
"Exception occurred:"
);
e
.
printStackTrace
(
System
.
out
);
}
}
}
public
void
shutdown
()
{
// TODO: Probably need to interrupt the thread. This is pretty
// insufficient, particularly for long-running queries.
done
=
true
;
}
}
public
SharedServerClient
(
String
hostname
,
int
port
)
throws
IOException
{
// Try to establish a connection to the shared database server.
socket
=
new
Socket
(
hostname
,
port
);
objectOutput
=
new
ObjectOutputStream
(
socket
.
getOutputStream
());
objectInput
=
new
ObjectInputStream
(
socket
.
getInputStream
());
semCommandDone
=
new
Semaphore
(
0
);
}
public
void
startup
()
{
// Start up the receiver thread that will print out whatever comes
// across the wire.
receiver
=
new
Receiver
(
System
.
out
);
receiverThread
=
new
Thread
(
receiver
);
receiverThread
.
start
();
}
public
CommandResult
handleCommand
(
String
command
)
{
try
{
objectOutput
.
writeObject
(
command
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"Unexpected error while transmitting command"
,
e
);
}
// Wait for the command to be completed.
try
{
semCommandDone
.
acquire
();
}
catch
(
InterruptedException
e
)
{
throw
new
RuntimeException
(
"Interrupted while waiting for command to finish"
,
e
);
}
return
null
;
}
public
void
shutdown
()
throws
IOException
{
receiver
.
shutdown
();
receiverThread
.
interrupt
();
objectInput
.
close
();
objectOutput
.
close
();
socket
.
close
();
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/client/package.html
0 → 100644
View file @
eb289261
<html>
<body>
This package contains classes that allow NanoDB to be used from multiple
concurrent clients, to maintain client session state, and so forth. Note that
this does not include important details such as transaction isolation or other
concurrency-control features; it is simply the mechanisms to support multiple
clients interacting with the database.
</body>
</html>
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/AnalyzeCommand.java
0 → 100755
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedHashSet
;
import
java.util.Set
;
import
edu.caltech.nanodb.relations.TableInfo
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
edu.caltech.nanodb.storage.StorageManager
;
import
edu.caltech.nanodb.storage.TableManager
;
/**
* This Command class represents the <tt>ANALYZE</tt> SQL command, which
* analyzes a table's internal data and updates its cached statistics to be as
* up-to-date as possible. This is not a standard SQL command, but virtually
* every good database has a mechanism to manually perform this task.
*/
public
class
AnalyzeCommand
extends
Command
{
/**
* Table names are kept in a set so that we don't need to worry about a
* particular table being specified multiple times.
*/
private
LinkedHashSet
<
String
>
tableNames
;
private
boolean
verbose
=
false
;
/**
* Construct a new <tt>ANALYZE</tt> command with an empty table list.
* Tables can be added to the internal list using the {@link #addTable}
* method.
*
* @param verbose a flag indicating whether this command should produce
* verbose output
*/
public
AnalyzeCommand
(
boolean
verbose
)
{
super
(
Command
.
Type
.
UTILITY
);
tableNames
=
new
LinkedHashSet
<>();
this
.
verbose
=
verbose
;
}
/**
* Construct a new <tt>ANALYZE</tt> command with an empty table list.
* Tables can be added to the internal list using the {@link #addTable}
* method.
*/
public
AnalyzeCommand
()
{
this
(
false
);
}
/**
* Construct a new <tt>ANALYZE</tt> command to analyze the specified table.
*
* @param tableName the name of the table to analyze.
*/
public
AnalyzeCommand
(
String
tableName
)
{
this
(
tableName
,
false
);
}
/**
* Construct a new <tt>ANALYZE</tt> command to analyze the specified table.
*
* @param tableName the name of the table to analyze.
* @param verbose a flag indicating whether this command should produce
* verbose output
*/
public
AnalyzeCommand
(
String
tableName
,
boolean
verbose
)
{
this
(
verbose
);
addTable
(
tableName
);
}
/**
* Add a table to the list of tables to analyze.
*
* @param tableName the name of the table to analyze.
*/
public
void
addTable
(
String
tableName
)
{
if
(
tableName
==
null
)
throw
new
NullPointerException
(
"tableName cannot be null"
);
tableNames
.
add
(
tableName
);
}
/**
* Returns the set of tables to analyze in an unmodifiable set.
*
* @return the set of tables to analyze in an unmodifiable set.
*/
public
Set
<
String
>
getTableNames
()
{
return
Collections
.
unmodifiableSet
(
tableNames
);
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
// Make sure that all the tables are valid.
StorageManager
storageManager
=
server
.
getStorageManager
();
TableManager
tableManager
=
storageManager
.
getTableManager
();
ArrayList
<
TableInfo
>
tableInfos
=
new
ArrayList
<>();
for
(
String
table
:
tableNames
)
{
TableInfo
tableInfo
=
tableManager
.
openTable
(
table
);
tableInfos
.
add
(
tableInfo
);
}
// Now, analyze each table.
for
(
TableInfo
tableInfo
:
tableInfos
)
{
out
.
println
(
"Analyzing table "
+
tableInfo
.
getTableName
());
tableManager
.
analyzeTable
(
tableInfo
);
if
(
verbose
)
{
// TODO: Implement
out
.
println
(
"TODO: Add verbose output... :-P"
);
}
}
out
.
println
(
"Analysis complete."
);
}
/**
* Prints a simple representation of the analyze command, including the
* names of the tables to be analyzed.
*
* @return a string representing this analyze command
*/
@Override
public
String
toString
()
{
return
"Analyze["
+
tableNames
+
"]"
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/BeginTransactionCommand.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
edu.caltech.nanodb.storage.StorageManager
;
import
edu.caltech.nanodb.transactions.TransactionException
;
/**
* This class represents a command that starts a transaction, such as
* <tt>BEGIN</tt>, <tt>BEGIN WORK</tt>, or <tt>START TRANSACTION</tt>.
*/
public
class
BeginTransactionCommand
extends
Command
{
public
BeginTransactionCommand
()
{
super
(
Type
.
UTILITY
);
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
// Begin a transaction.
try
{
// Pass true for the "user-started transaction" flag, since the
// user issued the command to do it!
StorageManager
storageManager
=
server
.
getStorageManager
();
storageManager
.
getTransactionManager
().
startTransaction
(
true
);
}
catch
(
TransactionException
e
)
{
throw
new
ExecutionException
(
e
);
}
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/Command.java
0 → 100755
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
edu.caltech.nanodb.server.SessionState
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
java.io.PrintStream
;
/**
* Abstract base-class for all commands that NanoDB supports. Command classes
* contain both the arguments and configuration details for the command being
* executed, as well as the code for actually performing the command. Databases
* tend to have large <tt>switch</tt> statements controlling how various
* commands are handled, and this really isn't a very pretty way to do things.
* So, NanoDB uses a class-hierarchy for command representation and execution.
* <p>
* The command class is subclassed into various command categories that relate
* to various operations in the database. For example, the {@link QueryCommand}
* class represents all <tt>SELECT</tt>, <tt>INSERT</tt>, <tt>UPDATE</tt>, and
* <tt>DELETE</tt> operations.
*/
public
abstract
class
Command
{
/**
* Commands are either Data-Definition Language (DDL), Data-Manipulation
* Language (DML), or utility commands.
*/
public
enum
Type
{
/** A Data Definition Language (DDL) command. */
DDL
,
/** A Data Manipulation Language (DML) command. */
DML
,
/** A utility command. */
UTILITY
}
/** The type of this command. */
private
Type
cmdType
;
/**
* This is the output stream for the current session, so that command
* output goes to the appropriate client.
*/
protected
PrintStream
out
;
/**
* Create a new command instance, of the specified command-type. The
* constructor is protected, but that is redundant with the fact that the
* class is abstract anyways, so this class cannot be constructed directly.
*
* @param cmdType the general category of command
*/
protected
Command
(
Type
cmdType
)
{
this
.
cmdType
=
cmdType
;
this
.
out
=
SessionState
.
get
().
getOutputStream
();
}
/** Returns the general type or category of this command. */
public
Type
getCommandType
()
{
return
cmdType
;
}
/**
* Actually performs the command.
*
* @throws ExecutionException if an issue occurs during command execution
*/
public
abstract
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
;
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CommandProperties.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.Set
;
import
edu.caltech.nanodb.expressions.TypeConverter
;
/**
* This class holds properties that might contain additional details for a
* command. For example, the <tt>CREATE TABLE</tt> DDL command takes
* properties to specify non-default storage engine types, the page size to
* use for the table file, and so forth.
*/
public
class
CommandProperties
{
/** A map of name-value pairs that represent the actual properties. */
private
HashMap
<
String
,
Object
>
values
=
new
HashMap
<>();
public
void
set
(
String
name
,
Object
value
)
{
if
(
name
==
null
)
throw
new
IllegalArgumentException
(
"name cannot be null"
);
if
(
value
==
null
)
throw
new
IllegalArgumentException
(
"value cannot be null"
);
values
.
put
(
name
,
value
);
}
public
Object
get
(
String
name
,
Object
defaultValue
)
{
Object
value
=
values
.
get
(
name
);
if
(
value
==
null
)
value
=
defaultValue
;
return
value
;
}
public
Object
get
(
String
name
)
{
return
get
(
name
,
null
);
}
public
int
getInt
(
String
name
,
int
defaultValue
)
{
Object
obj
=
get
(
name
);
if
(
obj
==
null
)
return
defaultValue
;
Integer
intObj
=
TypeConverter
.
getIntegerValue
(
obj
);
return
intObj
.
intValue
();
}
public
String
getString
(
String
name
,
String
defaultValue
)
{
Object
obj
=
get
(
name
);
if
(
obj
==
null
)
return
defaultValue
;
return
obj
.
toString
();
}
public
Set
<
String
>
getNames
()
{
return
Collections
.
unmodifiableSet
(
values
.
keySet
());
}
@Override
public
String
toString
()
{
return
"CommandProperties["
+
values
.
toString
()
+
"]"
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CommitTransactionCommand.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
edu.caltech.nanodb.storage.StorageManager
;
import
edu.caltech.nanodb.transactions.TransactionException
;
/**
* This class represents a command that commits a transaction, such as
* <tt>COMMIT</tt> or <tt>COMMIT WORK</tt>.
*/
public
class
CommitTransactionCommand
extends
Command
{
public
CommitTransactionCommand
()
{
super
(
Type
.
UTILITY
);
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
// Commit the transaction.
try
{
StorageManager
storageManager
=
server
.
getStorageManager
();
storageManager
.
getTransactionManager
().
commitTransaction
();
}
catch
(
TransactionException
e
)
{
throw
new
ExecutionException
(
e
);
}
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/ConstraintDecl.java
0 → 100755
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
edu.caltech.nanodb.relations.ForeignKeyValueChangeOption
;
import
edu.caltech.nanodb.relations.TableConstraintType
;
/**
* Constraints may be specified at the table level, or they may be specified
* on individual columns. Obviously, the kinds of constraint allowed depends
* on what the constraint is associated with.
*/
public
class
ConstraintDecl
{
/**
* The optional name of the constraint, or {@code null} if no name was
* specified.
*/
private
String
name
=
null
;
/** The type of the constraint. */
private
TableConstraintType
type
;
/**
* Flag indicating whether the constraint is specified at the table-level
* or at the column-level. A value of {@code true} indicates that it is a
* column-level constraint; a value of {@code false} indicates that it is
* a table-level constraint.
*/
private
boolean
columnConstraint
;
/**
* For {@link TableConstraintType#UNIQUE} and
* {@link TableConstraintType#PRIMARY_KEY} constraints, this is a list of
* one or more column names that are constrained. Note that for a column
* constraint, this list will contain <i>exactly</i> one column name.
* For a table-level constraint, this list will contain one or more column
* names.
* <p>
* For any other constraint type, this will be set to <tt>null</tt>.
*/
private
List
<
String
>
columnNames
=
new
ArrayList
<>();
/**
* For {@link TableConstraintType#FOREIGN_KEY} constraints, this is the
* table that is referenced by the column.
* <p>
* For any other constraint type, this will be set to <tt>null</tt>.
*/
private
String
refTableName
=
null
;
/**
* For {@link TableConstraintType#FOREIGN_KEY} constraints, this is a list
* of one or more column names in the reference-table that are referenced
* by the foreign-key constraint. Note that for a column-constraint, this
* list will contain <i>exactly</i> one column-name. For a
* table-constraint, this list will contain one or more column-names.
* <p>
* For any other constraint type, this will be set to <code>null</code>.
*/
private
List
<
String
>
refColumnNames
=
new
ArrayList
<>();
/**
* For {@link TableConstraintType#FOREIGN_KEY} constraints, this is the
* {@link edu.caltech.nanodb.relations.ForeignKeyValueChangeOption} for
* <tt>ON DELETE</tt> behavior.
* <p>
* For any other constraint type, this will be set to <tt>null</tt>.
*/
private
ForeignKeyValueChangeOption
onDeleteOption
=
null
;
/**
* For {@link TableConstraintType#FOREIGN_KEY} constraints, this is the
* {@link edu.caltech.nanodb.relations.ForeignKeyValueChangeOption} for
* <tt>ON UPDATE</tt> behavior.
* <p>
* For any other constraint type, this will be set to <tt>null</tt>.
*/
private
ForeignKeyValueChangeOption
onUpdateOption
=
null
;
/** Create a new unnamed constraint for a table or a table-column. */
public
ConstraintDecl
(
TableConstraintType
type
,
boolean
columnConstraint
)
{
this
(
type
,
null
,
columnConstraint
);
}
/** Create a new named constraint for a table or a table-column. */
public
ConstraintDecl
(
TableConstraintType
type
,
String
name
,
boolean
columnConstraint
)
{
this
.
type
=
type
;
this
.
name
=
name
;
this
.
columnConstraint
=
columnConstraint
;
}
/**
* Returns the name of the constraint.
* @return the name of the constraint.
*/
public
String
getName
()
{
return
name
;
}
/** Returns the type of this constraint. */
public
TableConstraintType
getType
()
{
return
type
;
}
/**
* Returns <tt>true</tt> if this constraint is associated with a
* table-column, or <tt>false</tt> if it is a table-level constraint.
*/
public
boolean
isColumnConstraint
()
{
return
columnConstraint
;
}
/**
* Add a column to the constraint. This specifies that the constraint
* governs values in the column. For column-level constraints, only a
* single column may be specified. For table-level constraints, one or more
* columns may be specified.
*
* @param columnName the column governed by the constraint
*
* @throws NullPointerException if columnName is <tt>null</tt>
* @throws IllegalStateException if this is a column-constraint and
* there is already one column specified
*
* @design (donnie) Column names are checked for existence and uniqueness
* when initializing the corresponding objects for storage on the
* table schema. See
* {@link edu.caltech.nanodb.relations.Schema#addCandidateKey} and
* {@link edu.caltech.nanodb.relations.Schema#addForeignKey}
* for details.
*/
public
void
addColumn
(
String
columnName
)
{
if
(
columnName
==
null
)
throw
new
NullPointerException
(
"columnName"
);
if
(
columnNames
.
size
()
==
1
&&
isColumnConstraint
())
{
throw
new
IllegalStateException
(
"Cannot specify multiple columns in a column-constraint."
);
}
columnNames
.
add
(
columnName
);
}
public
List
<
String
>
getColumnNames
()
{
return
Collections
.
unmodifiableList
(
columnNames
);
}
/**
* Add a reference-table to a {@link TableConstraintType#FOREIGN_KEY}
* constraint. This specifies the table that constrains the values in
* the column.
*
* @param tableName the table referenced in the constraint
*
* @throws NullPointerException if tableName is <tt>null</tt>
* @throws IllegalStateException if this constraint is not a foreign-key
* constraint
*
* @design (donnie) Existence of the referenced table is checked in the
* {@link CreateTableCommand#execute} method's operation.
*/
public
void
setRefTable
(
String
tableName
)
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"Reference tables are only specified on FOREIGN_KEY constraints."
);
}
if
(
tableName
==
null
)
throw
new
IllegalArgumentException
(
"tableName must be specified"
);
refTableName
=
tableName
;
}
/**
* Returns the name of the referenced table for a
* {@link TableConstraintType#FOREIGN_KEY} constraint.
*
* @return the name of the referenced table for a FOREIGN_KEY constraint.
*/
public
String
getRefTable
()
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"Reference tables are only specified on FOREIGN_KEY constraints."
);
}
return
refTableName
;
}
/**
* Add a reference-column to a {@link TableConstraintType#FOREIGN_KEY}
* constraint. This specifies the column that constrains the values in
* the column. For column-level constraints, only a single column may be
* specified. For table-level constraints, one or more columns may be
* specified.
*
* @param columnName the column referenced in the constraint
*
* @throws NullPointerException if columnName is <tt>null</tt>
* @throws IllegalStateException if this constraint is not a foreign-key
* constraint, or if this is a column-constraint and there is
* already one reference-column specified
*
* @design (donnie) Column names are checked for existence and uniqueness
* when initializing the corresponding objects for storage on the
* table schema. See
* {@link edu.caltech.nanodb.relations.Schema#addCandidateKey} and
* {@link edu.caltech.nanodb.relations.Schema#addForeignKey}
* for details.
*/
public
void
addRefColumn
(
String
columnName
)
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"Reference columns only allowed on FOREIGN_KEY constraints."
);
}
if
(
columnName
==
null
)
throw
new
NullPointerException
(
"columnName"
);
if
(
refColumnNames
.
size
()
==
1
&&
isColumnConstraint
())
{
throw
new
IllegalStateException
(
"Cannot specify multiple "
+
"reference-columns in a column-constraint."
);
}
refColumnNames
.
add
(
columnName
);
}
public
List
<
String
>
getRefColumnNames
()
{
return
Collections
.
unmodifiableList
(
refColumnNames
);
}
/**
* Add an <tt>ON DELETE</tt> option to a
* {@link TableConstraintType#FOREIGN_KEY} constraint.
*
* @param f the {@link ForeignKeyValueChangeOption} to set for
* <tt>ON DELETE</tt> behavior
*
* @throws IllegalStateException if this constraint is not a foreign-key
* constraint
*/
public
void
setOnDeleteOption
(
ForeignKeyValueChangeOption
f
)
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"ON DELETE only specified on FOREIGN_KEY constraints."
);
}
onDeleteOption
=
f
;
}
/**
* Returns the <tt>ON DELETE</tt> {@link ForeignKeyValueChangeOption} for
* a {@link TableConstraintType#FOREIGN_KEY} constraint.
*
* @return the <tt>ON DELETE</tt> {@link ForeignKeyValueChangeOption}
*/
public
ForeignKeyValueChangeOption
getOnDeleteOption
()
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"ON DELETE only specified on FOREIGN_KEY constraints."
);
}
return
onDeleteOption
;
}
/**
* Add an <tt>ON UPDATE</tt> option to a
* {@link TableConstraintType#FOREIGN_KEY} constraint.
*
* @param f the {@link ForeignKeyValueChangeOption} to set for
* <tt>ON UPDATE</tt> behavior
*
* @throws IllegalStateException if this constraint is not a foreign-key
* constraint
*/
public
void
setOnUpdateOption
(
ForeignKeyValueChangeOption
f
)
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"ON UPDATE only specified on FOREIGN_KEY constraints."
);
}
onUpdateOption
=
f
;
}
/**
* Returns the <tt>ON UPDATE</tt> {@link ForeignKeyValueChangeOption} for
* a {@link TableConstraintType#FOREIGN_KEY} constraint.
*
* @return the <tt>ON UPDATE</tt> {@link ForeignKeyValueChangeOption}
*/
public
ForeignKeyValueChangeOption
getOnUpdateOption
()
{
if
(
type
!=
TableConstraintType
.
FOREIGN_KEY
)
{
throw
new
IllegalStateException
(
"ON UPDATE only specified on FOREIGN_KEY constraints."
);
}
return
onUpdateOption
;
}
@Override
public
String
toString
()
{
return
"Constraint["
+
(
name
!=
null
?
name
:
"(unnamed)"
)
+
" : "
+
type
+
", "
+
(
isColumnConstraint
()
?
"column"
:
"table"
)
+
"]"
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CrashCommand.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
edu.caltech.nanodb.server.NanoDBServer
;
/**
* This command "crashes" the database by shutting it down immediately without
* any proper cleanup or flushing of caches.
*/
public
class
CrashCommand
extends
Command
{
/**
* Stores how many seconds to wait before crashing the database. A value
* of 0 means "crash immediately."
*/
private
int
secondsToCrash
;
/**
* Construct a new <tt>CRASH</tt> command that will wait for the specified
* number of seconds and then crash the database.
*/
public
CrashCommand
(
int
secs
)
{
super
(
Command
.
Type
.
UTILITY
);
secondsToCrash
=
secs
;
}
/**
* Construct a new <tt>CRASH</tt> command that will crash the database
* immediately.
*/
public
CrashCommand
()
{
this
(
0
);
}
public
int
getSecondsToCrash
()
{
return
secondsToCrash
;
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
if
(
secondsToCrash
<=
0
)
{
// Crash immediately.
doCrash
();
}
else
{
// Wait for the specified amount of time in a thread, then crash.
Thread
t
=
new
Thread
(
new
Runnable
()
{
public
void
run
()
{
try
{
Thread
.
sleep
(
secondsToCrash
*
1000
);
out
.
println
(
"\n"
);
doCrash
();
}
catch
(
InterruptedException
e
)
{
out
.
println
(
"Crash-thread was interrupted, not crashing."
);
}
}
});
t
.
start
();
}
}
private
void
doCrash
()
{
out
.
println
(
"Goodbye, cruel world! I'm taking your data with me!!!"
);
// Using this API call avoids running shutdown hooks, finalizers, etc.
// 22 is the exit status of the VM's process
Runtime
.
getRuntime
().
halt
(
22
);
}
/**
* Prints a simple representation of the crash command.
*
* @return a string representing this crash command
*/
@Override
public
String
toString
()
{
String
s
=
"Crash"
;
if
(
secondsToCrash
>
0
)
s
+=
"[wait "
+
secondsToCrash
+
" seconds]"
;
return
s
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CreateIndexCommand.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.indexes.IndexManager
;
import
edu.caltech.nanodb.relations.ColumnRefs
;
import
edu.caltech.nanodb.relations.IndexColumnRefs
;
import
edu.caltech.nanodb.relations.KeyColumnRefs
;
import
edu.caltech.nanodb.relations.TableConstraintType
;
import
edu.caltech.nanodb.relations.TableInfo
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
edu.caltech.nanodb.storage.TableManager
;
import
edu.caltech.nanodb.storage.StorageManager
;
/** This command-class represents the <tt>CREATE INDEX</tt> DDL command. */
public
class
CreateIndexCommand
extends
Command
{
/** A logging object for reporting anything interesting that happens. **/
private
static
Logger
logger
=
LogManager
.
getLogger
(
CreateIndexCommand
.
class
);
/** The name of the index being created. */
private
String
indexName
;
/**
* This flag specifies whether the index is a unique index or not. If the
* value is true then no key-value may appear multiple times; if the value
* is false then a key-value may appear multiple times.
*/
private
boolean
unique
;
/**
* If this flag is {@code true} then the create-index operation should
* only be performed if the specified index doesn't already exist.
*/
private
boolean
ifNotExists
;
/** The name of the table that the index is built against. */
private
String
tableName
;
/**
* The list of column-names that the index is built against. The order of
* these values is important; for ordered indexes, the index records must be
* kept in the order specified by the sequence of column names.
*/
private
ArrayList
<
String
>
columnNames
=
new
ArrayList
<>();
/** Any additional properties specified in the command. */
private
CommandProperties
properties
;
public
CreateIndexCommand
(
String
indexName
,
String
tableName
,
boolean
unique
)
{
super
(
Type
.
DDL
);
if
(
tableName
==
null
)
throw
new
IllegalArgumentException
(
"tableName cannot be null"
);
this
.
indexName
=
indexName
;
this
.
tableName
=
tableName
;
this
.
unique
=
unique
;
}
/**
* Returns {@code true} if index creation should only be attempted if it
* doesn't already exist, {@code false} if index creation should always
* be attempted.
*
* @return {@code true} if index creation should only be attempted if it
* doesn't already exist, {@code false} if index creation should
* always be attempted.
*/
public
boolean
getIfNotExists
()
{
return
ifNotExists
;
}
/**
* Sets the flag indicating whether index creation should only be
* attempted if it doesn't already exist.
*
* @param b the flag indicating whether index creation should only be
* attempted if it doesn't already exist.
*/
public
void
setIfNotExists
(
boolean
b
)
{
ifNotExists
=
b
;
}
/**
* Get the name of the table containing the index to be dropped.
*
* @return the name of the table containing the index to drop
*/
public
String
getTableName
()
{
return
tableName
;
}
/**
* Get the name of the index to be dropped.
*
* @return the name of the index to drop
*/
public
String
getIndexName
()
{
return
indexName
;
}
/**
* Returns the list of column names specified for the index as an
* unmodifiable list.
*
* @return the list of column names specified for the index as an
* unmodifiable list.
*/
public
List
<
String
>
getColumnNames
()
{
return
Collections
.
unmodifiableList
(
columnNames
);
}
/**
* Returns true if the requested index is a unique index; false otherwise.
*
* @return true if the requested index is a unique index; false otherwise.
*/
public
boolean
isUnique
()
{
return
unique
;
}
/**
* Sets any additional properties associated with the command. The
* value may be {@code null} to indicate no properties.
*
* @param properties any additional properties to associate with the
* command.
*/
public
void
setProperties
(
CommandProperties
properties
)
{
this
.
properties
=
properties
;
}
/**
* Returns any additional properties specified for the command, or
* {@code null} if no additional properties were specified.
*
* @return any additional properties specified for the command, or
* {@code null} if no additional properties were specified.
*/
public
CommandProperties
getProperties
()
{
return
properties
;
}
/**
* Adds a column to the list of columns to be included in the index.
*
* @param columnName the name of the column to add to the columns to be
* included in the index.
*/
public
void
addColumn
(
String
columnName
)
{
if
(
columnName
==
null
)
throw
new
IllegalArgumentException
(
"columnName cannot be null"
);
this
.
columnNames
.
add
(
columnName
);
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
StorageManager
storageManager
=
server
.
getStorageManager
();
TableManager
tableManager
=
storageManager
.
getTableManager
();
IndexManager
indexManager
=
storageManager
.
getIndexManager
();
// Open the table and get the schema for the table.
logger
.
debug
(
String
.
format
(
"Opening table %s to retrieve schema"
,
tableName
));
TableInfo
tableInfo
=
tableManager
.
openTable
(
tableName
);
int
[]
cols
=
tableInfo
.
getSchema
().
getColumnIndexes
(
columnNames
);
IndexColumnRefs
index
=
new
IndexColumnRefs
(
indexName
,
cols
);
if
(
unique
)
{
// Also record a candidate key on the columns.
KeyColumnRefs
key
=
new
KeyColumnRefs
(
cols
,
indexName
,
TableConstraintType
.
UNIQUE
);
}
indexManager
.
addIndexToTable
(
tableInfo
,
index
);
logger
.
debug
(
String
.
format
(
"New index %s on table %s is created!"
,
indexName
,
tableName
));
out
.
printf
(
"Created index %s on table %s.%n"
,
indexName
,
tableName
);
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CreateTableCommand.java
0 → 100755
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.LogManager
;
import
edu.caltech.nanodb.indexes.IndexManager
;
import
edu.caltech.nanodb.relations.ColumnInfo
;
import
edu.caltech.nanodb.relations.ForeignKeyColumnRefs
;
import
edu.caltech.nanodb.relations.IndexColumnRefs
;
import
edu.caltech.nanodb.relations.KeyColumnRefs
;
import
edu.caltech.nanodb.relations.Schema
;
import
edu.caltech.nanodb.relations.TableConstraintType
;
import
edu.caltech.nanodb.relations.TableInfo
;
import
edu.caltech.nanodb.server.NanoDBServer
;
import
edu.caltech.nanodb.server.properties.PropertyRegistry
;
import
edu.caltech.nanodb.storage.TableManager
;
import
edu.caltech.nanodb.storage.StorageManager
;
/**
* This command handles the <tt>CREATE TABLE</tt> DDL operation.
*/
public
class
CreateTableCommand
extends
Command
{
/** A logging object for reporting anything interesting that happens. */
private
static
Logger
logger
=
LogManager
.
getLogger
(
CreateTableCommand
.
class
);
/** Name of the table to be created. */
private
String
tableName
;
/** If this flag is {@code true} then the table is a temporary table. */
private
boolean
temporary
;
/**
* If this flag is {@code true} then the create-table operation should
* only be performed if the specified table doesn't already exist.
*/
private
boolean
ifNotExists
;
/** List of column-declarations for the new table. */
private
List
<
ColumnInfo
>
columnInfos
=
new
ArrayList
<>();
/** List of constraints for the new table. */
private
List
<
ConstraintDecl
>
constraints
=
new
ArrayList
<>();
/** Any additional properties specified in the command. */
private
CommandProperties
properties
;
/**
* Create a new object representing a <tt>CREATE TABLE</tt> statement.
*
* @param tableName the name of the table to be created
*/
public
CreateTableCommand
(
String
tableName
)
{
super
(
Command
.
Type
.
DDL
);
if
(
tableName
==
null
)
throw
new
IllegalArgumentException
(
"tableName cannot be null"
);
this
.
tableName
=
tableName
;
}
/**
* Returns {@code true} if the table is a temporary table, {@code false}
* otherwise.
*
* @return {@code true} if the table is a temporary table, {@code false}
* otherwise.
*/
public
boolean
isTemporary
()
{
return
temporary
;
}
/**
* Specifies whether the table is a temporary table or not.
*
* @param b {@code true} if the table is a temporary table, {@code false}
* otherwise.
*/
public
void
setTemporary
(
boolean
b
)
{
temporary
=
b
;
}
/**
* Returns {@code true} if table creation should only be attempted if it
* doesn't already exist, {@code false} if table creation should always
* be attempted.
*
* @return {@code true} if table creation should only be attempted if it
* doesn't already exist, {@code false} if table creation should
* always be attempted.
*/
public
boolean
getIfNotExists
()
{
return
ifNotExists
;
}
/**
* Sets the flag indicating whether table creation should only be
* attempted if it doesn't already exist.
*
* @param b the flag indicating whether table creation should only be
* attempted if it doesn't already exist.
*/
public
void
setIfNotExists
(
boolean
b
)
{
ifNotExists
=
b
;
}
/**
* Sets any additional properties associated with the command. The
* value may be {@code null} to indicate no properties.
*
* @param properties any additional properties to associate with the
* command.
*/
public
void
setProperties
(
CommandProperties
properties
)
{
this
.
properties
=
properties
;
}
/**
* Returns any additional properties specified for the command, or
* {@code null} if no additional properties were specified.
*
* @return any additional properties specified for the command, or
* {@code null} if no additional properties were specified.
*/
public
CommandProperties
getProperties
()
{
return
properties
;
}
/**
* Adds a column description to this create-table command. This method is
* primarily used by the SQL parser.
*
* @param colDecl the details of the column to add
*
* @throws NullPointerException if colDecl is null
*/
public
void
addColumn
(
TableColumnDecl
colDecl
)
{
if
(
colDecl
==
null
)
throw
new
IllegalArgumentException
(
"colDecl cannot be null"
);
// Pull out the column-info value first, and add it to the table
// specification. If the table-name doesn't match, update this.
ColumnInfo
colInfo
=
colDecl
.
getColumnInfo
();
if
(!
tableName
.
equals
(
colInfo
.
getTableName
()))
{
colInfo
=
new
ColumnInfo
(
colInfo
.
getName
(),
tableName
,
colInfo
.
getType
());
}
columnInfos
.
add
(
colInfo
);
// Next, if any column-level constraints are specified,
// add them to the collection of constraints.
for
(
ConstraintDecl
constraint
:
colDecl
.
getConstraints
())
addConstraint
(
constraint
);
}
/**
* Returns an immutable list of the column descriptions that are part of
* this <tt>CREATE TABLE</tt> command.
*
* @return an immutable list of the column descriptions that are part of
* this <tt>CREATE TABLE</tt> command.
*/
public
List
<
ColumnInfo
>
getColumns
()
{
return
Collections
.
unmodifiableList
(
columnInfos
);
}
/**
* Adds a constraint to this create-table command. This method is primarily
* used by the SQL parser.
*
* @param con the details of the table constraint to add
*
* @throws NullPointerException if con is null
*/
public
void
addConstraint
(
ConstraintDecl
con
)
{
if
(
con
==
null
)
throw
new
IllegalArgumentException
(
"con cannot be null"
);
constraints
.
add
(
con
);
}
/**
* Returns an immutable list of the constraint declarations that are part
* of this <tt>CREATE TABLE</tt> command.
*
* @return an immutable list of the constraint declarations that are part
* of this <tt>CREATE TABLE</tt> command.
*/
public
List
<
ConstraintDecl
>
getConstraints
()
{
return
Collections
.
unmodifiableList
(
constraints
);
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
StorageManager
storageManager
=
server
.
getStorageManager
();
TableManager
tableManager
=
storageManager
.
getTableManager
();
PropertyRegistry
propReg
=
server
.
getPropertyRegistry
();
boolean
createIndexesOnKeys
=
propReg
.
getBooleanProperty
(
NanoDBServer
.
PROP_CREATE_INDEXES_ON_KEYS
);
// See if the table already exists.
if
(
ifNotExists
&&
tableManager
.
tableExists
(
tableName
))
{
out
.
printf
(
"Table %s already exists; skipping create-table.%n"
,
tableName
);
return
;
}
// Set up the table's schema based on the command details.
logger
.
debug
(
"Creating a TableSchema object for the new table "
+
tableName
+
"."
);
Schema
schema
=
new
Schema
();
for
(
ColumnInfo
colInfo
:
columnInfos
)
{
try
{
schema
.
addColumnInfo
(
colInfo
);
}
catch
(
IllegalArgumentException
iae
)
{
throw
new
ExecutionException
(
"Duplicate or invalid column \""
+
colInfo
.
getName
()
+
"\"."
,
iae
);
}
}
// Do some basic verification of the table constraints:
// * Verify that all named constraints are uniquely named.
// * Open all tables referenced by foreign-key constraints, to ensure
// they exist. (More verification will occur later.)
HashSet
<
String
>
constraintNames
=
new
HashSet
<>();
HashMap
<
String
,
TableInfo
>
referencedTables
=
new
HashMap
<>();
for
(
ConstraintDecl
cd:
constraints
)
{
String
name
=
cd
.
getName
();
if
(
name
!=
null
&&
!
constraintNames
.
add
(
name
))
{
throw
new
ExecutionException
(
"Constraint name "
+
name
+
" appears multiple times."
);
}
if
(
cd
.
getType
()
==
TableConstraintType
.
FOREIGN_KEY
)
{
String
refTableName
=
cd
.
getRefTable
();
TableInfo
refTblInfo
=
tableManager
.
openTable
(
refTableName
);
if
(
refTblInfo
==
null
)
{
throw
new
ExecutionException
(
"Referenced table "
+
refTableName
+
" doesn't exist."
);
}
referencedTables
.
put
(
refTableName
,
refTblInfo
);
}
}
// Initialize all the constraints on the table.
initTableConstraints
(
storageManager
,
schema
,
referencedTables
);
// Create the table.
logger
.
debug
(
"Creating the new table "
+
tableName
+
" on disk."
);
TableInfo
tableInfo
=
tableManager
.
createTable
(
tableName
,
schema
,
properties
);
logger
.
debug
(
"New table "
+
tableName
+
" was created."
);
if
(
createIndexesOnKeys
)
{
logger
.
debug
(
"Creating indexes on the new table, and any "
+
"referenced tables"
);
initIndexes
(
storageManager
,
tableInfo
);
}
out
.
println
(
"Created table: "
+
tableName
);
}
private
void
initTableConstraints
(
StorageManager
storageManager
,
Schema
schema
,
HashMap
<
String
,
TableInfo
>
referencedTables
)
{
if
(
constraints
.
isEmpty
())
{
logger
.
debug
(
"No table constraints specified, our work is done."
);
return
;
}
TableManager
tableManager
=
storageManager
.
getTableManager
();
logger
.
debug
(
"Adding "
+
constraints
.
size
()
+
" constraints to the table."
);
HashSet
<
String
>
constraintNames
=
new
HashSet
<>();
for
(
ConstraintDecl
cd
:
constraints
)
{
// Make sure that if constraint names are specified, every
// constraint is actually uniquely named.
if
(
cd
.
getName
()
!=
null
)
{
if
(!
constraintNames
.
add
(
cd
.
getName
()))
{
throw
new
ExecutionException
(
"Constraint name "
+
cd
.
getName
()
+
" appears multiple times."
);
}
}
TableConstraintType
type
=
cd
.
getType
();
if
(
type
==
TableConstraintType
.
PRIMARY_KEY
||
type
==
TableConstraintType
.
UNIQUE
)
{
// Make a candidate-key constraint and put it on the schema.
int
[]
cols
=
schema
.
getColumnIndexes
(
cd
.
getColumnNames
());
KeyColumnRefs
ck
=
new
KeyColumnRefs
(
cols
,
cd
.
getName
(),
type
);
if
(
type
==
TableConstraintType
.
PRIMARY_KEY
)
{
// Add NOT NULL constraints for all primary-key columns.
for
(
int
iCol
:
cols
)
schema
.
addNotNull
(
iCol
);
}
schema
.
addCandidateKey
(
ck
);
}
else
if
(
type
==
TableConstraintType
.
FOREIGN_KEY
)
{
// Make a foreign key constraint and put it on the schema.
// This involves these steps:
// 1) Create the foreign key on this table's schema.
// 2) Update the referenced table's schema to record that
// this table references that table.
// This should never be null since we already resolved all
// foreign-key table references earlier.
TableInfo
refTableInfo
=
referencedTables
.
get
(
cd
.
getRefTable
());
Schema
refSchema
=
refTableInfo
.
getSchema
();
// The makeForeignKey() method ensures that the referenced
// columns are also a candidate key (or primary key) on the
// referenced table.
ForeignKeyColumnRefs
fk
=
DDLUtils
.
makeForeignKey
(
schema
,
refTableInfo
,
cd
);
schema
.
addForeignKey
(
fk
);
// Update the referenced table's schema to record that this
// table has a foreign-key reference to the table.
if
(
refSchema
.
addReferencingTable
(
tableName
))
tableManager
.
saveTableInfo
(
refTableInfo
);
}
else
if
(
type
==
TableConstraintType
.
NOT_NULL
)
{
int
idx
=
schema
.
getColumnIndex
(
cd
.
getColumnNames
().
get
(
0
));
schema
.
addNotNull
(
idx
);
}
else
{
throw
new
ExecutionException
(
"Unexpected constraint type "
+
cd
.
getType
());
}
}
}
private
void
initIndexes
(
StorageManager
storageManager
,
TableInfo
tableInfo
)
{
IndexManager
indexManager
=
storageManager
.
getIndexManager
();
Schema
schema
=
tableInfo
.
getSchema
();
for
(
ConstraintDecl
cd
:
constraints
)
{
TableConstraintType
type
=
cd
.
getType
();
if
(
type
==
TableConstraintType
.
PRIMARY_KEY
||
type
==
TableConstraintType
.
UNIQUE
)
{
int
[]
cols
=
schema
.
getColumnIndexes
(
cd
.
getColumnNames
());
IndexColumnRefs
ck
=
new
IndexColumnRefs
(
cd
.
getName
(),
cols
);
// Make the index. This also updates the table schema with
// the fact that there is another candidate key on the table.
indexManager
.
addIndexToTable
(
tableInfo
,
ck
);
}
else
if
(
type
==
TableConstraintType
.
FOREIGN_KEY
)
{
// Check if there is already an index on the foreign-key
// columns. If there is not, we will create a non-unique
// index on those columns.
int
[]
fkCols
=
schema
.
getColumnIndexes
(
cd
.
getColumnNames
());
IndexColumnRefs
fkColRefs
=
null
;
// schema.getIndexOnColumns(new ColumnRefs(fkCols));
if
(
fkColRefs
==
null
)
{
// Need to make a new index for this foreign-key reference
fkColRefs
=
new
IndexColumnRefs
(
cd
.
getName
(),
fkCols
);
fkColRefs
.
setConstraintType
(
TableConstraintType
.
FOREIGN_KEY
);
// Make the index.
indexManager
.
addIndexToTable
(
tableInfo
,
fkColRefs
);
logger
.
debug
(
String
.
format
(
"Created index %s on table %s to enforce foreign key."
,
fkColRefs
.
getIndexName
(),
tableInfo
.
getTableName
()));
}
}
}
}
@Override
public
String
toString
()
{
return
"CreateTable["
+
tableName
+
"]"
;
}
/**
* Returns a verbose, multi-line string containing all of the details of
* this table.
*
* @return a detailed description of the table described by this command
*/
public
String
toVerboseString
()
{
StringBuilder
strBuf
=
new
StringBuilder
();
strBuf
.
append
(
toString
());
strBuf
.
append
(
'\n'
);
for
(
ColumnInfo
colInfo
:
columnInfos
)
{
strBuf
.
append
(
'\t'
);
strBuf
.
append
(
colInfo
.
toString
());
strBuf
.
append
(
'\n'
);
}
for
(
ConstraintDecl
con
:
constraints
)
{
strBuf
.
append
(
'\t'
);
strBuf
.
append
(
con
.
toString
());
strBuf
.
append
(
'\n'
);
}
return
strBuf
.
toString
();
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/CreateViewCommand.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
edu.caltech.nanodb.queryast.SelectClause
;
import
edu.caltech.nanodb.server.NanoDBServer
;
/**
* This command-class represents the <tt>CREATE VIEW</tt> DDL command.
*/
public
class
CreateViewCommand
extends
Command
{
private
String
viewName
;
private
SelectClause
selectClause
;
public
CreateViewCommand
(
String
viewName
,
SelectClause
selectClause
)
{
super
(
Type
.
DDL
);
if
(
viewName
==
null
)
throw
new
IllegalArgumentException
(
"viewName cannot be null"
);
if
(
selectClause
==
null
)
throw
new
IllegalArgumentException
(
"selectClause cannot be null"
);
this
.
viewName
=
viewName
;
this
.
selectClause
=
selectClause
;
}
@Override
public
void
execute
(
NanoDBServer
server
)
throws
ExecutionException
{
throw
new
ExecutionException
(
"Not yet implemented!"
);
}
}
This diff is collapsed.
Click to expand it.
src/main/java/edu/caltech/nanodb/commands/DDLUtils.java
0 → 100644
View file @
eb289261
package
edu.caltech.nanodb.commands
;
import
java.util.ArrayList
;
import
java.util.List
;
import
edu.caltech.nanodb.relations.ColumnInfo
;
import
edu.caltech.nanodb.relations.ColumnType
;
import
edu.caltech.nanodb.relations.ForeignKeyColumnRefs
;
import
edu.caltech.nanodb.relations.Schema
;
import
edu.caltech.nanodb.relations.SchemaNameException
;
import
edu.caltech.nanodb.relations.TableInfo
;
/**
* This helper class provides some useful functions for constructing keys and
* indexes and other details for tables that are being initialized.
*/
public
class
DDLUtils
{
/**
* This method constructs a {@link ForeignKeyColumnRefs} object that
* includes the columns named in the input list, as well as the referenced
* table and column names. Note that this method <u>does not</u> update
* the schema stored on disk, or create any other supporting files.
*
* @param tableSchema the schema of the table that the foreign key
* will be added to
*
* @param refTableInfo the table that the foreign key will reference
*
* @param constraintDecl the parsed constraint declaration describing
* the foreign key
*
* @return an object describing the foreign key
*
* @throws SchemaNameException if a column-name cannot be found, or if a
* column-name is ambiguous (unlikely), or if a column is
* specified multiple times in the input list.
*/
public
static
ForeignKeyColumnRefs
makeForeignKey
(
Schema
tableSchema
,
TableInfo
refTableInfo
,
ConstraintDecl
constraintDecl
)
{
// if (tableInfo == null)
// throw new IllegalArgumentException("tableInfo must be specified");
if
(
refTableInfo
==
null
)
throw
new
IllegalArgumentException
(
"refTableInfo must be specified"
);
if
(
constraintDecl
==
null
)
throw
new
IllegalArgumentException
(
"constraintDecl must be specified"
);
Schema
refTableSchema
=
refTableInfo
.
getSchema
();
int
[]
colIndexes
=
tableSchema
.
getColumnIndexes
(
constraintDecl
.
getColumnNames
());
List
<
String
>
refColumnNames
=
constraintDecl
.
getRefColumnNames
();
int
[]
refColIndexes
;
if
(
refColumnNames
.
isEmpty
())
{
// The constraint declaration doesn't specify column names because
// it wants to use the primary key of the referenced table.
refColIndexes
=
refTableSchema
.
getPrimaryKey
().
getCols
();
}
else
{
// The constraint declaration specifies column names, so convert
// those names into the corresponding column indexes.
refColIndexes
=
refTableSchema
.
getColumnIndexes
(
refColumnNames
);
}
if
(!
refTableSchema
.
hasKeyOnColumns
(
refColIndexes
))
{
throw
new
SchemaNameException
(
String
.
format
(
"Referenced columns %s in table %s are not a candidate key"
,
refColumnNames
,
refTableInfo
.
getTableName
()));
}
ArrayList
<
ColumnInfo
>
colInfos
=
tableSchema
.
getColumnInfos
(
colIndexes
);
ArrayList
<
ColumnInfo
>
refColInfos
=
refTableSchema
.
getColumnInfos
(
refColIndexes
);
// Check if the corresponding columns in the FK are the same types
for
(
int
i
=
0
;
i
<
colInfos
.
size
();
i
++)
{
ColumnType
type
=
colInfos
.
get
(
i
).
getType
();
ColumnType
refType
=
refColInfos
.
get
(
i
).
getType
();
if
(!
type
.
equals
(
refType
))
{
throw
new
IllegalArgumentException
(
"columns in "
+
"child and parent tables of the foreign key must be "
+
"of the same type!"
);
}
}
// The onDelete and onUpdate values could be null if they are
// unspecified in the constructor. They are set to
// ForeignKeyValueChangeOption.RESTRICT as a default in this case in
// the constructor for ForeignKeyColumnIndexes.
ForeignKeyColumnRefs
fk
=
new
ForeignKeyColumnRefs
(
colIndexes
,
refTableInfo
.
getTableName
(),
refColIndexes
,
constraintDecl
.
getOnDeleteOption
(),
constraintDecl
.
getOnUpdateOption
());
fk
.
setConstraintName
(
constraintDecl
.
getName
());
return
fk
;
}
}
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
6
7
…
13
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help