提交 cceb5a12 authored 作者: noelgrandin's avatar noelgrandin

Issue 510: Make org.h2.bnf public for consumption by external projects, patch by Nicolas Fortin

上级 5da851fe
...@@ -489,7 +489,7 @@ CREATE DOMAIN EMAIL AS VARCHAR(255) CHECK (POSITION('@', VALUE) > 1) ...@@ -489,7 +489,7 @@ CREATE DOMAIN EMAIL AS VARCHAR(255) CHECK (POSITION('@', VALUE) > 1)
"Commands (DDL)","CREATE INDEX"," "Commands (DDL)","CREATE INDEX","
CREATE CREATE
{ [ UNIQUE ] [ HASH ] INDEX [ [ IF NOT EXISTS ] newIndexName ] { [ UNIQUE ] [ HASH ] [ SPATIAL] INDEX [ [ IF NOT EXISTS ] newIndexName ]
| PRIMARY KEY [ HASH ] } | PRIMARY KEY [ HASH ] }
ON tableName ( indexColumn [,...] ) ON tableName ( indexColumn [,...] )
"," ","
...@@ -500,6 +500,7 @@ Hash indexes are meant for in-memory databases and memory tables (CREATE MEMORY ...@@ -500,6 +500,7 @@ Hash indexes are meant for in-memory databases and memory tables (CREATE MEMORY
For other tables, or if the index contains multiple columns, the HASH keyword is ignored. For other tables, or if the index contains multiple columns, the HASH keyword is ignored.
Hash indexes can only test for equality, and do not support range queries (similar to a hash table). Hash indexes can only test for equality, and do not support range queries (similar to a hash table).
Non-unique keys are supported. Non-unique keys are supported.
Spatial indexes are supported only on Geometry columns.
"," ","
CREATE INDEX IDXNAME ON TEST(NAME) CREATE INDEX IDXNAME ON TEST(NAME)
" "
...@@ -1726,9 +1727,9 @@ multiple lines long. ...@@ -1726,9 +1727,9 @@ multiple lines long.
" "
"Other Grammar","Compare"," "Other Grammar","Compare","
<> | <= | >= | = | < | > | != <> | <= | >= | = | < | > | != | &&
"," ","
Comparison operator. The operator != is the same as <>. Comparison operator. The operator != is the same as <>. && means overlapping, can only be used with geometry types.
"," ","
<> <>
" "
......
...@@ -80,6 +80,7 @@ Change Log ...@@ -80,6 +80,7 @@ Change Log
</li><li>Make org.h2.util.ScriptReader throw a better exception when handling broken scripts which generate </li><li>Make org.h2.util.ScriptReader throw a better exception when handling broken scripts which generate
extremely large statements. extremely large statements.
</li><li>Fix bug with ALLOW_LITERALS=NONE, where the periodic analyze table on insert would throw an exception. </li><li>Fix bug with ALLOW_LITERALS=NONE, where the periodic analyze table on insert would throw an exception.
</li><li>Issue 510: Make org.h2.bnf public for consumption by external projects, patch by Nicolas Fortin
</li></ul> </li></ul>
<h2>Version 1.3.173 (2013-07-28)</h2> <h2>Version 1.3.173 (2013-07-28)</h2>
......
...@@ -48,5 +48,7 @@ Export-Package: org.h2;version="${version}", ...@@ -48,5 +48,7 @@ Export-Package: org.h2;version="${version}",
org.h2.jdbcx;version="${version}", org.h2.jdbcx;version="${version}",
org.h2.tools;version="${version}", org.h2.tools;version="${version}",
org.h2.util;version="${version}", org.h2.util;version="${version}",
org.h2.value;version="${version}" org.h2.value;version="${version}",
org.h2.bnf;version="${version}" ,
org.h2.bnf.context;version="${version}"
Premain-Class: org.h2.util.Profiler Premain-Class: org.h2.util.Profiler
...@@ -15,7 +15,8 @@ import java.sql.SQLException; ...@@ -15,7 +15,8 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.h2.server.web.DbContextRule;
import org.h2.bnf.context.DbContextRule;
import org.h2.tools.Csv; import org.h2.tools.Csv;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
......
...@@ -13,14 +13,14 @@ import org.h2.util.StringUtils; ...@@ -13,14 +13,14 @@ import org.h2.util.StringUtils;
/** /**
* A single terminal rule in a BNF object. * A single terminal rule in a BNF object.
*/ */
class RuleElement implements Rule { public class RuleElement implements Rule {
private final boolean keyword; private final boolean keyword;
private final String name; private final String name;
private Rule link; private Rule link;
private final int type; private final int type;
RuleElement(String name, String topic) { public RuleElement(String name, String topic) {
this.name = name; this.name = name;
this.keyword = name.length() == 1 || name.equals(StringUtils.toUpperEnglish(name)); this.keyword = name.length() == 1 || name.equals(StringUtils.toUpperEnglish(name));
topic = StringUtils.toLowerEnglish(topic); topic = StringUtils.toLowerEnglish(topic);
......
...@@ -13,13 +13,13 @@ import org.h2.util.New; ...@@ -13,13 +13,13 @@ import org.h2.util.New;
/** /**
* Represents a sequence of BNF rules, or a list of alternative rules. * Represents a sequence of BNF rules, or a list of alternative rules.
*/ */
class RuleList implements Rule { public class RuleList implements Rule {
private final boolean or; private final boolean or;
private final ArrayList<Rule> list; private final ArrayList<Rule> list;
private boolean mapSet; private boolean mapSet;
RuleList(Rule first, Rule next, boolean or) { public RuleList(Rule first, Rule next, boolean or) {
list = New.arrayList(); list = New.arrayList();
if (first instanceof RuleList && ((RuleList) first).or == or) { if (first instanceof RuleList && ((RuleList) first).or == or) {
list.addAll(((RuleList) first).list); list.addAll(((RuleList) first).list);
......
...@@ -11,11 +11,11 @@ import java.util.HashMap; ...@@ -11,11 +11,11 @@ import java.util.HashMap;
/** /**
* Represents an optional BNF rule. * Represents an optional BNF rule.
*/ */
class RuleOptional implements Rule { public class RuleOptional implements Rule {
private final Rule rule; private final Rule rule;
private boolean mapSet; private boolean mapSet;
RuleOptional(Rule rule) { public RuleOptional(Rule rule) {
this.rule = rule; this.rule = rule;
} }
......
...@@ -11,12 +11,12 @@ import java.util.HashMap; ...@@ -11,12 +11,12 @@ import java.util.HashMap;
/** /**
* Represents a loop in a BNF object. * Represents a loop in a BNF object.
*/ */
class RuleRepeat implements Rule { public class RuleRepeat implements Rule {
private final Rule rule; private final Rule rule;
private final boolean comma; private final boolean comma;
RuleRepeat(Rule rule, boolean comma) { public RuleRepeat(Rule rule, boolean comma) {
this.rule = rule; this.rule = rule;
this.comma = comma; this.comma = comma;
} }
......
...@@ -9,8 +9,8 @@ package org.h2.bnf; ...@@ -9,8 +9,8 @@ package org.h2.bnf;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import org.h2.server.web.DbSchema; import org.h2.bnf.context.DbSchema;
import org.h2.server.web.DbTableOrView; import org.h2.bnf.context.DbTableOrView;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -29,12 +29,12 @@ public class Sentence { ...@@ -29,12 +29,12 @@ public class Sentence {
/** /**
* The token type for a keyword. * The token type for a keyword.
*/ */
static final int KEYWORD = 1; public static final int KEYWORD = 1;
/** /**
* The token type for a function name. * The token type for a function name.
*/ */
static final int FUNCTION = 2; public static final int FUNCTION = 2;
private static final long MAX_PROCESSING_TIME = 100; private static final long MAX_PROCESSING_TIME = 100;
...@@ -63,7 +63,7 @@ public class Sentence { ...@@ -63,7 +63,7 @@ public class Sentence {
/** /**
* Start the timer to make sure processing doesn't take too long. * Start the timer to make sure processing doesn't take too long.
*/ */
void start() { public void start() {
stopAt = System.currentTimeMillis() + MAX_PROCESSING_TIME; stopAt = System.currentTimeMillis() + MAX_PROCESSING_TIME;
} }
...@@ -72,7 +72,7 @@ public class Sentence { ...@@ -72,7 +72,7 @@ public class Sentence {
* Processing auto-complete shouldn't take more than a few milliseconds. * Processing auto-complete shouldn't take more than a few milliseconds.
* If processing is stopped, this methods throws an IllegalStateException * If processing is stopped, this methods throws an IllegalStateException
*/ */
void stopIfRequired() { public void stopIfRequired() {
if (System.currentTimeMillis() > stopAt) { if (System.currentTimeMillis() > stopAt) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
...@@ -214,7 +214,7 @@ public class Sentence { ...@@ -214,7 +214,7 @@ public class Sentence {
* *
* @return the next token map * @return the next token map
*/ */
HashMap<String, String> getNext() { public HashMap<String, String> getNext() {
return next; return next;
} }
......
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.server.web; package org.h2.bnf.context;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
/** /**
* Keeps the meta data information of a column. * Keeps the meta data information of a column.
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
class DbColumn { public class DbColumn {
/** private final String name;
* The column name.
*/ private final String quotedName;
final String name;
private final String dataType;
/**
* The quoted table name. private int position;
*/
final String quotedName; /**
* @return The data type name (including precision and the NOT NULL flag if
/** * applicable).
* The data type name (including precision and the NOT NULL flag if */
* applicable). public String getDataType() {
*/ return dataType;
final String dataType; }
DbColumn(DbContents contents, ResultSet rs) throws SQLException { /**
name = rs.getString("COLUMN_NAME"); * @return The column name.
quotedName = contents.quoteIdentifier(name); */
String type = rs.getString("TYPE_NAME"); public String getName() {
int size = rs.getInt(DbContents.findColumn(rs, "COLUMN_SIZE", 7)); return name;
boolean isSQLite = contents.isSQLite; }
if (size > 0 && !isSQLite) {
type += "(" + size; /**
int prec = rs.getInt(DbContents.findColumn(rs, "DECIMAL_DIGITS", 9)); * @return The quoted table name.
if (prec > 0) { */
type += ", " + prec; public String getQuotedName() {
} return quotedName;
type += ")"; }
}
if (rs.getInt("NULLABLE") == DatabaseMetaData.columnNoNulls) { /**
type += " NOT NULL"; * @return Column index
} */
dataType = type; public int getPosition() {
} return position;
} }
public DbColumn(DbContents contents, ResultSet rs) throws SQLException {
name = rs.getString("COLUMN_NAME");
quotedName = contents.quoteIdentifier(name);
String type = rs.getString("TYPE_NAME");
// A procedures column size is identified by PRECISION, for table this is COLUMN_SIZE
int precisionColumnIndex = DbContents.findColumn(rs, "PRECISION", 0);
int size;
if( precisionColumnIndex == 0 ){
size = rs.getInt(DbContents.findColumn(rs, "COLUMN_SIZE", 7));
} else {
size = rs.getInt(precisionColumnIndex);
}
position = rs.getInt("ORDINAL_POSITION");
boolean isSQLite = contents.isSQLite();
if (size > 0 && !isSQLite) {
type += "(" + size;
int prec = rs.getInt(DbContents.findColumn(rs, "DECIMAL_DIGITS", 9));
if (prec > 0) {
type += ", " + prec;
}
type += ")";
}
if (rs.getInt("NULLABLE") == DatabaseMetaData.columnNoNulls) {
type += " NOT NULL";
}
dataType = type;
}
}
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.server.web; package org.h2.bnf.context;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.command.Parser; import org.h2.command.Parser;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
* Keeps meta data information about a database. * Keeps meta data information about a database.
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
public class DbContents { public class DbContents {
/** private DbSchema[] schemas;
* The list of schemas. private DbSchema defaultSchema;
*/ private boolean isOracle;
DbSchema[] schemas; private boolean isH2;
private boolean isPostgreSQL;
/** private boolean isDerby;
* The default schema. private boolean isSQLite;
*/ private boolean isH2ModeMySQL;
DbSchema defaultSchema; private boolean isMySQL;
private boolean isFirebird;
/** private boolean isMSSQLServer;
* True if this is an Oracle database.
*/ /**
boolean isOracle; * @return The default schema.
*/
/** public DbSchema getDefaultSchema() {
* True if this is a H2 database. return defaultSchema;
*/ }
boolean isH2;
/**
/** * @return True if this is an Apache Derby database.
* True if this is a PostgreSQL database. */
*/ public boolean isDerby() {
boolean isPostgreSQL; return isDerby;
}
/**
* True if this is an Apache Derby database. /**
*/ * @return True if this is a Firebird database.
boolean isDerby; */
public boolean isFirebird() {
/** return isFirebird;
* True if this is an SQLite database. }
*/
boolean isSQLite; /**
* @return True if this is a H2 database.
/** */
* True if this is a H2 database in MySQL mode. public boolean isH2() {
*/ return isH2;
private boolean isH2ModeMySQL; }
/** /**
* True if this is a MySQL database. * @return True if this is a H2 database in MySQL mode.
*/ */
private boolean isMySQL; public boolean isH2ModeMySQL() {
return isH2ModeMySQL;
/** }
* True if this is a Firebird database.
*/ /**
private boolean isFirebird; * @return True if this is a MS SQL Server database.
*/
/** public boolean isMSSQLServer() {
* True if this is a MS SQL Server database. return isMSSQLServer;
*/ }
private boolean isMSSQLServer;
/**
/** * @return True if this is a MySQL database.
* Get the column index of a column in a result set. If the column is not */
* found, the default column index is returned. public boolean isMySQL() {
* This is a workaround for a JDBC-ODBC bridge problem. return isMySQL;
* }
* @param rs the result set
* @param columnName the column name /**
* @param defaultColumnIndex the default column index * @return True if this is an Oracle database.
* @return the column index */
*/ public boolean isOracle() {
static int findColumn(ResultSet rs, String columnName, int defaultColumnIndex) { return isOracle;
try { }
return rs.findColumn(columnName);
} catch (SQLException e) { /**
return defaultColumnIndex; * @return True if this is a PostgreSQL database.
} */
} public boolean isPostgreSQL() {
return isPostgreSQL;
/** }
* Read the contents of this database from the database meta data.
* /**
* @param meta the database meta data * @return True if this is an SQLite database.
*/ */
synchronized void readContents(DatabaseMetaData meta) throws SQLException { public boolean isSQLite() {
String prod = StringUtils.toLowerEnglish(meta.getDatabaseProductName()); return isSQLite;
isSQLite = prod.indexOf("sqlite") >= 0; }
String url = meta.getURL();
if (url != null) { /**
isH2 = url.startsWith("jdbc:h2:"); * @return The list of schemas.
if (isH2) { */
Statement stat = meta.getConnection().createStatement(); public DbSchema[] getSchemas() {
ResultSet rs = stat.executeQuery( return schemas;
"SELECT UPPER(VALUE) FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME='MODE'"); }
rs.next();
if ("MYSQL".equals(rs.getString(1))) { /**
isH2ModeMySQL = true; * Get the column index of a column in a result set. If the column is not
} * found, the default column index is returned.
rs.close(); * This is a workaround for a JDBC-ODBC bridge problem.
stat.close(); *
} * @param rs the result set
isOracle = url.startsWith("jdbc:oracle:"); * @param columnName the column name
isPostgreSQL = url.startsWith("jdbc:postgresql:"); * @param defaultColumnIndex the default column index
// isHSQLDB = url.startsWith("jdbc:hsqldb:"); * @return the column index
isMySQL = url.startsWith("jdbc:mysql:"); */
isDerby = url.startsWith("jdbc:derby:"); public static int findColumn(ResultSet rs, String columnName, int defaultColumnIndex) {
isFirebird = url.startsWith("jdbc:firebirdsql:"); try {
isMSSQLServer = url.startsWith("jdbc:sqlserver:"); return rs.findColumn(columnName);
} } catch (SQLException e) {
String defaultSchemaName = getDefaultSchemaName(meta); return defaultColumnIndex;
String[] schemaNames = getSchemaNames(meta); }
schemas = new DbSchema[schemaNames.length]; }
for (int i = 0; i < schemaNames.length; i++) {
String schemaName = schemaNames[i]; /**
boolean isDefault = defaultSchemaName == null || defaultSchemaName.equals(schemaName); * Read the contents of this database from the database meta data.
DbSchema schema = new DbSchema(this, schemaName, isDefault); *
if (schema.isDefault) { * @param meta the database meta data
defaultSchema = schema; */
} public synchronized void readContents(DatabaseMetaData meta) throws SQLException {
schemas[i] = schema; String prod = StringUtils.toLowerEnglish(meta.getDatabaseProductName());
String[] tableTypes = { "TABLE", "SYSTEM TABLE", "VIEW", "SYSTEM VIEW", isSQLite = prod.indexOf("sqlite") >= 0;
"TABLE LINK", "SYNONYM", "EXTERNAL" }; String url = meta.getURL();
schema.readTables(meta, tableTypes); if (url != null) {
} isH2 = url.startsWith("jdbc:h2:");
if (defaultSchema == null) { if (isH2) {
String best = null; Statement stat = meta.getConnection().createStatement();
for (DbSchema schema : schemas) { ResultSet rs = stat.executeQuery(
if ("dbo".equals(schema.name)) { "SELECT UPPER(VALUE) FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME='MODE'");
// MS SQL Server rs.next();
defaultSchema = schema; if ("MYSQL".equals(rs.getString(1))) {
break; isH2ModeMySQL = true;
} }
if (defaultSchema == null || best == null || schema.name.length() < best.length()) { rs.close();
best = schema.name; stat.close();
defaultSchema = schema; }
} isOracle = url.startsWith("jdbc:oracle:");
} isPostgreSQL = url.startsWith("jdbc:postgresql:");
} // isHSQLDB = url.startsWith("jdbc:hsqldb:");
} isMySQL = url.startsWith("jdbc:mysql:");
isDerby = url.startsWith("jdbc:derby:");
private String[] getSchemaNames(DatabaseMetaData meta) throws SQLException { isFirebird = url.startsWith("jdbc:firebirdsql:");
if (isMySQL || isSQLite) { isMSSQLServer = url.startsWith("jdbc:sqlserver:");
return new String[] { "" }; }
} else if (isFirebird) { String defaultSchemaName = getDefaultSchemaName(meta);
return new String[] { null }; String[] schemaNames = getSchemaNames(meta);
} schemas = new DbSchema[schemaNames.length];
ResultSet rs = meta.getSchemas(); for (int i = 0; i < schemaNames.length; i++) {
ArrayList<String> schemaList = New.arrayList(); String schemaName = schemaNames[i];
while (rs.next()) { boolean isDefault = defaultSchemaName == null || defaultSchemaName.equals(schemaName);
String schema = rs.getString(findColumn(rs, "TABLE_SCHEM", 1)); DbSchema schema = new DbSchema(this, schemaName, isDefault);
if (isOracle) { if (schema.isDefault) {
for (String ignore : new String[] { defaultSchema = schema;
"CTXSYS", "DIP", "DBSNMP", "DMSYS", "EXFSYS", "FLOWS_020100", "FLOWS_FILES", "MDDATA", "MDSYS", }
"MGMT_VIEW", "OLAPSYS", "ORDSYS", "ORDPLUGINS", "OUTLN", "SI_INFORMTN_SCHEMA", "SYS", "SYSMAN", schemas[i] = schema;
"SYSTEM", "TSMSYS", "WMSYS", "XDB" String[] tableTypes = { "TABLE", "SYSTEM TABLE", "VIEW", "SYSTEM VIEW",
}) { "TABLE LINK", "SYNONYM", "EXTERNAL" };
if (ignore.equals(schema)) { schema.readTables(meta, tableTypes);
schema = null; schema.readProcedures(meta);
break; }
} if (defaultSchema == null) {
} String best = null;
} else if (isMSSQLServer) { for (DbSchema schema : schemas) {
for (String ignore : new String[] { if ("dbo".equals(schema.name)) {
"sys", "db_accessadmin", "db_backupoperator", "db_datareader", "db_datawriter", "db_ddladmin", // MS SQL Server
"db_denydatareader", "db_denydatawriter", "db_owner", "db_securityadmin" defaultSchema = schema;
}) { break;
if (ignore.equals(schema)) { }
schema = null; if (defaultSchema == null || best == null || schema.name.length() < best.length()) {
break; best = schema.name;
} defaultSchema = schema;
} }
} }
if (schema == null) { }
continue; }
}
schemaList.add(schema); private String[] getSchemaNames(DatabaseMetaData meta) throws SQLException {
} if (isMySQL || isSQLite) {
rs.close(); return new String[] { "" };
String[] list = new String[schemaList.size()]; } else if (isFirebird) {
schemaList.toArray(list); return new String[] { null };
return list; }
} ResultSet rs = meta.getSchemas();
ArrayList<String> schemaList = New.arrayList();
private String getDefaultSchemaName(DatabaseMetaData meta) { while (rs.next()) {
String defaultSchemaName = ""; String schema = rs.getString(findColumn(rs, "TABLE_SCHEM", 1));
try { if (isOracle) {
if (isOracle) { for (String ignore : new String[] {
return meta.getUserName(); "CTXSYS", "DIP", "DBSNMP", "DMSYS", "EXFSYS", "FLOWS_020100", "FLOWS_FILES", "MDDATA", "MDSYS",
} else if (isPostgreSQL) { "MGMT_VIEW", "OLAPSYS", "ORDSYS", "ORDPLUGINS", "OUTLN", "SI_INFORMTN_SCHEMA", "SYS", "SYSMAN",
return "public"; "SYSTEM", "TSMSYS", "WMSYS", "XDB"
} else if (isMySQL) { }) {
return ""; if (ignore.equals(schema)) {
} else if (isDerby) { schema = null;
return StringUtils.toUpperEnglish(meta.getUserName()); break;
} else if (isFirebird) { }
return null; }
} } else if (isMSSQLServer) {
ResultSet rs = meta.getSchemas(); for (String ignore : new String[] {
int index = rs.findColumn("IS_DEFAULT"); "sys", "db_accessadmin", "db_backupoperator", "db_datareader", "db_datawriter", "db_ddladmin",
while (rs.next()) { "db_denydatareader", "db_denydatawriter", "db_owner", "db_securityadmin"
if (rs.getBoolean(index)) { }) {
defaultSchemaName = rs.getString("TABLE_SCHEM"); if (ignore.equals(schema)) {
} schema = null;
} break;
} catch (SQLException e) { }
// IS_DEFAULT not found }
} }
return defaultSchemaName; if (schema == null) {
} continue;
}
/** schemaList.add(schema);
* Add double quotes around an identifier if required. }
* For the H2 database, all identifiers are quoted. rs.close();
* String[] list = new String[schemaList.size()];
* @param identifier the identifier schemaList.toArray(list);
* @return the quoted identifier return list;
*/ }
String quoteIdentifier(String identifier) {
if (identifier == null) { private String getDefaultSchemaName(DatabaseMetaData meta) {
return null; String defaultSchemaName = "";
} try {
if (isH2 && !isH2ModeMySQL) { if (isOracle) {
return Parser.quoteIdentifier(identifier); return meta.getUserName();
} } else if (isPostgreSQL) {
return StringUtils.toUpperEnglish(identifier); return "public";
} } else if (isMySQL) {
return "";
} } else if (isDerby) {
return StringUtils.toUpperEnglish(meta.getUserName());
} else if (isFirebird) {
return null;
}
ResultSet rs = meta.getSchemas();
int index = rs.findColumn("IS_DEFAULT");
while (rs.next()) {
if (rs.getBoolean(index)) {
defaultSchemaName = rs.getString("TABLE_SCHEM");
}
}
} catch (SQLException e) {
// IS_DEFAULT not found
}
return defaultSchemaName;
}
/**
* Add double quotes around an identifier if required.
* For the H2 database, all identifiers are quoted.
*
* @param identifier the identifier
* @return the quoted identifier
*/
public String quoteIdentifier(String identifier) {
if (identifier == null) {
return null;
}
if (isH2 && !isH2ModeMySQL) {
return Parser.quoteIdentifier(identifier);
}
return StringUtils.toUpperEnglish(identifier);
}
}
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.server.web; package org.h2.bnf.context;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import org.h2.bnf.BnfVisitor; import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule; import org.h2.bnf.Rule;
import org.h2.bnf.RuleHead; import org.h2.bnf.RuleElement;
import org.h2.bnf.Sentence; import org.h2.bnf.RuleHead;
import org.h2.command.Parser; import org.h2.bnf.RuleList;
import org.h2.message.DbException; import org.h2.bnf.Sentence;
import org.h2.util.StringUtils; import org.h2.command.Parser;
import org.h2.message.DbException;
/** import org.h2.util.StringUtils;
* A BNF terminal rule that is linked to the database context information.
* This class is used by the H2 Console, to support auto-complete. /**
*/ * A BNF terminal rule that is linked to the database context information.
public class DbContextRule implements Rule { * This class is used by the H2 Console, to support auto-complete.
*/
static final int COLUMN = 0, TABLE = 1, TABLE_ALIAS = 2; public class DbContextRule implements Rule {
static final int NEW_TABLE_ALIAS = 3;
static final int COLUMN_ALIAS = 4, SCHEMA = 5; public static final int COLUMN = 0, TABLE = 1, TABLE_ALIAS = 2;
public static final int NEW_TABLE_ALIAS = 3;
private final DbContents contents; public static final int COLUMN_ALIAS = 4, SCHEMA = 5, PROCEDURE = 6;
private final int type;
private final DbContents contents;
DbContextRule(DbContents contents, int type) { private final int type;
this.contents = contents;
this.type = type; private String columnType;
}
/**
@Override * BNF terminal rule Constructor
public void setLinks(HashMap<String, RuleHead> ruleMap) { * @param contents Extract rule from this component
// nothing to do * @param type Rule type, one of {@link DbContextRule#COLUMN,DbContextRule#TABLE,DbContextRule#TABLE_ALIAS,DbContextRule#NEW_TABLE_ALIAS,DbContextRule#COLUMN_ALIAS,DbContextRule#SCHEMA}
} */
public DbContextRule(DbContents contents, int type) {
@Override this.contents = contents;
public void accept(BnfVisitor visitor) { this.type = type;
// nothing to do }
}
/**
@Override * @param columnType COLUMN Auto completion can be filtered by column type
public boolean autoComplete(Sentence sentence) { */
String query = sentence.getQuery(), s = query; public void setColumnType(String columnType) {
String up = sentence.getQueryUpper(); this.columnType = columnType;
switch (type) { }
case SCHEMA: {
DbSchema[] schemas = contents.schemas; @Override
String best = null; public void setLinks(HashMap<String, RuleHead> ruleMap) {
DbSchema bestSchema = null; // nothing to do
for (DbSchema schema: schemas) { }
String name = StringUtils.toUpperEnglish(schema.name);
if (up.startsWith(name)) { @Override
if (best == null || name.length() > best.length()) { public void accept(BnfVisitor visitor) {
best = name; // nothing to do
bestSchema = schema; }
}
} else if (s.length() == 0 || name.startsWith(up)) { @Override
if (s.length() < name.length()) { public boolean autoComplete(Sentence sentence) {
sentence.add(name, name.substring(s.length()), type); String query = sentence.getQuery(), s = query;
sentence.add(schema.quotedName + ".", schema.quotedName.substring(s.length()) + ".", Sentence.CONTEXT); String up = sentence.getQueryUpper();
} switch (type) {
} case SCHEMA: {
} DbSchema[] schemas = contents.getSchemas();
if (best != null) { String best = null;
sentence.setLastMatchedSchema(bestSchema); DbSchema bestSchema = null;
s = s.substring(best.length()); for (DbSchema schema: schemas) {
} String name = StringUtils.toUpperEnglish(schema.name);
break; if (up.startsWith(name)) {
} if (best == null || name.length() > best.length()) {
case TABLE: { best = name;
DbSchema schema = sentence.getLastMatchedSchema(); bestSchema = schema;
if (schema == null) { }
schema = contents.defaultSchema; } else if (s.length() == 0 || name.startsWith(up)) {
} if (s.length() < name.length()) {
DbTableOrView[] tables = schema.tables; sentence.add(name, name.substring(s.length()), type);
String best = null; sentence.add(schema.quotedName + ".", schema.quotedName.substring(s.length()) + ".", Sentence.CONTEXT);
DbTableOrView bestTable = null; }
for (DbTableOrView table : tables) { }
String compare = up; }
String name = StringUtils.toUpperEnglish(table.name); if (best != null) {
if (table.quotedName.length() > name.length()) { sentence.setLastMatchedSchema(bestSchema);
name = table.quotedName; s = s.substring(best.length());
compare = query; }
} break;
if (compare.startsWith(name)) { }
if (best == null || name.length() > best.length()) { case TABLE: {
best = name; DbSchema schema = sentence.getLastMatchedSchema();
bestTable = table; if (schema == null) {
} schema = contents.getDefaultSchema();
} else if (s.length() == 0 || name.startsWith(compare)) { }
if (s.length() < name.length()) { DbTableOrView[] tables = schema.getTables();
sentence.add(table.quotedName, table.quotedName.substring(s.length()), Sentence.CONTEXT); String best = null;
} DbTableOrView bestTable = null;
} for (DbTableOrView table : tables) {
} String compare = up;
if (best != null) { String name = StringUtils.toUpperEnglish(table.getName());
sentence.setLastMatchedTable(bestTable); if (table.getQuotedName().length() > name.length()) {
sentence.addTable(bestTable); name = table.getQuotedName();
s = s.substring(best.length()); compare = query;
} }
break; if (compare.startsWith(name)) {
} if (best == null || name.length() > best.length()) {
case NEW_TABLE_ALIAS: best = name;
s = autoCompleteTableAlias(sentence, true); bestTable = table;
break; }
case TABLE_ALIAS: } else if (s.length() == 0 || name.startsWith(compare)) {
s = autoCompleteTableAlias(sentence, false); if (s.length() < name.length()) {
break; sentence.add(table.getQuotedName(), table.getQuotedName().substring(s.length()), Sentence.CONTEXT);
case COLUMN_ALIAS: { }
int i = 0; }
if (query.indexOf(' ') < 0) { }
break; if (best != null) {
} sentence.setLastMatchedTable(bestTable);
for (; i < up.length(); i++) { sentence.addTable(bestTable);
char ch = up.charAt(i); s = s.substring(best.length());
if (ch != '_' && !Character.isLetterOrDigit(ch)) { }
break; break;
} }
} case NEW_TABLE_ALIAS:
if (i == 0) { s = autoCompleteTableAlias(sentence, true);
break; break;
} case TABLE_ALIAS:
String alias = up.substring(0, i); s = autoCompleteTableAlias(sentence, false);
if (Parser.isKeyword(alias, true)) { break;
break; case COLUMN_ALIAS: {
} int i = 0;
s = s.substring(alias.length()); if (query.indexOf(' ') < 0) {
break; break;
} }
case COLUMN: { for (; i < up.length(); i++) {
HashSet<DbTableOrView> set = sentence.getTables(); char ch = up.charAt(i);
String best = null; if (ch != '_' && !Character.isLetterOrDigit(ch)) {
DbTableOrView last = sentence.getLastMatchedTable(); break;
if (last != null && last.columns != null) { }
for (DbColumn column : last.columns) { }
String compare = up; if (i == 0) {
String name = StringUtils.toUpperEnglish(column.name); break;
if (column.quotedName.length() > name.length()) { }
name = column.quotedName; String alias = up.substring(0, i);
compare = query; if (Parser.isKeyword(alias, true)) {
} break;
if (compare.startsWith(name)) { }
String b = s.substring(name.length()); s = s.substring(alias.length());
if (best == null || b.length() < best.length()) { break;
best = b; }
} else if (s.length() == 0 || name.startsWith(compare)) { case COLUMN: {
if (s.length() < name.length()) { HashSet<DbTableOrView> set = sentence.getTables();
sentence.add(column.name, column.name.substring(s.length()), Sentence.CONTEXT); String best = null;
} DbTableOrView last = sentence.getLastMatchedTable();
} if (last != null && last.getColumns() != null) {
} for (DbColumn column : last.getColumns()) {
} String compare = up;
} String name = StringUtils.toUpperEnglish(column.getName());
for (DbSchema schema : contents.schemas) { if (column.getQuotedName().length() > name.length()) {
for (DbTableOrView table : schema.tables) { name = column.getQuotedName();
if (table != last && set != null && !set.contains(table)) { compare = query;
continue; }
} if (compare.startsWith(name) && (columnType == null || column.getDataType().contains(columnType))) {
if (table == null || table.columns == null) { String b = s.substring(name.length());
continue; if (best == null || b.length() < best.length()) {
} best = b;
for (DbColumn column : table.columns) { } else if (s.length() == 0 || name.startsWith(compare)) {
String name = StringUtils.toUpperEnglish(column.name); if (s.length() < name.length()) {
if (up.startsWith(name)) { sentence.add(column.getName(), column.getName().substring(s.length()), Sentence.CONTEXT);
String b = s.substring(name.length()); }
if (best == null || b.length() < best.length()) { }
best = b; }
} }
} else if (s.length() == 0 || name.startsWith(up)) { }
if (s.length() < name.length()) { for (DbSchema schema : contents.getSchemas()) {
sentence.add(column.name, column.name.substring(s.length()), Sentence.CONTEXT); for (DbTableOrView table : schema.getTables()) {
} if (table != last && set != null && !set.contains(table)) {
} continue;
} }
} if (table == null || table.getColumns() == null) {
} continue;
if (best != null) { }
s = best; for (DbColumn column : table.getColumns()) {
} String name = StringUtils.toUpperEnglish(column.getName());
break; if(columnType == null || column.getDataType().contains(columnType)) {
} if (up.startsWith(name)) {
default: String b = s.substring(name.length());
throw DbException.throwInternalError("type=" + type); if (best == null || b.length() < best.length()) {
} best = b;
if (!s.equals(query)) { }
while (s.length() > 0 && Character.isSpaceChar(s.charAt(0))) { } else if (s.length() == 0 || name.startsWith(up)) {
s = s.substring(1); if (s.length() < name.length()) {
} sentence.add(column.getName(), column.getName().substring(s.length()), Sentence.CONTEXT);
sentence.setQuery(s); }
return true; }
} }
return false; }
} }
}
private static String autoCompleteTableAlias(Sentence sentence, boolean newAlias) { if (best != null) {
String s = sentence.getQuery(); s = best;
String up = sentence.getQueryUpper(); }
int i = 0; break;
for (; i < up.length(); i++) { }
char ch = up.charAt(i); case PROCEDURE:
if (ch != '_' && !Character.isLetterOrDigit(ch)) { autoCompleteProcedure(sentence);
break; break;
} default:
} throw DbException.throwInternalError("type=" + type);
if (i == 0) { }
return s; if (!s.equals(query)) {
} while (s.length() > 0 && Character.isSpaceChar(s.charAt(0))) {
String alias = up.substring(0, i); s = s.substring(1);
if ("SET".equals(alias) || Parser.isKeyword(alias, true)) { }
return s; sentence.setQuery(s);
} return true;
if (newAlias) { }
sentence.addAlias(alias, sentence.getLastTable()); return false;
} }
HashMap<String, DbTableOrView> map = sentence.getAliases(); private void autoCompleteProcedure(Sentence sentence) {
if ((map != null && map.containsKey(alias)) || (sentence.getLastTable() == null)) { DbSchema schema = sentence.getLastMatchedSchema();
if (newAlias && s.length() == alias.length()) { if (schema == null) {
return s; schema = contents.getDefaultSchema();
} }
s = s.substring(alias.length()); String incompleteSentence = sentence.getQueryUpper();
if (s.length() == 0) { String incompleteFunctionName = incompleteSentence;
sentence.add(alias + ".", ".", Sentence.CONTEXT); if(incompleteSentence.contains("(")) {
} incompleteFunctionName = incompleteSentence.substring(0, incompleteSentence.indexOf('(')).trim();
return s; }
}
HashSet<DbTableOrView> tables = sentence.getTables(); // Common elements
if (tables != null) { RuleElement openBracket = new RuleElement("(", "Function");
String best = null; RuleElement closeBracket = new RuleElement(")", "Function");
for (DbTableOrView table : tables) { RuleElement comma = new RuleElement(",", "Function");
String tableName = StringUtils.toUpperEnglish(table.name);
if (alias.startsWith(tableName) && (best == null || tableName.length() > best.length())) { // Fetch all elements
sentence.setLastMatchedTable(table); for(DbProcedure procedure : schema.getProcedures()) {
best = tableName; final String procName = procedure.getName();
} else if (s.length() == 0 || tableName.startsWith(alias)) { if(procName.startsWith(incompleteFunctionName)) {
sentence.add(tableName + ".", tableName.substring(s.length()) + ".", Sentence.CONTEXT); // That's it, build a RuleList from this function
} RuleElement procedureElement = new RuleElement(procName, "Function");
} RuleList rl = new RuleList(procedureElement, openBracket, false);
if (best != null) { // Go further only if the user use open bracket
s = s.substring(best.length()); if(incompleteSentence.contains("(")) {
if (s.length() == 0) { for(DbColumn parameter : procedure.getParameters()) {
sentence.add(alias + ".", ".", Sentence.CONTEXT); if(parameter.getPosition() > 1) {
} rl = new RuleList(rl, comma, false);
return s; }
} DbContextRule columnRule = new DbContextRule(contents, COLUMN);
} String parameterType = parameter.getDataType();
return s; // Remove precision
} if(parameterType.contains("(")) {
parameterType = parameterType.substring(0, parameterType.indexOf('('));
} }
columnRule.setColumnType(parameterType);
rl = new RuleList(rl, columnRule , false);
}
rl = new RuleList(rl, closeBracket , false);
}
rl.autoComplete(sentence);
}
}
}
private static String autoCompleteTableAlias(Sentence sentence, boolean newAlias) {
String s = sentence.getQuery();
String up = sentence.getQueryUpper();
int i = 0;
for (; i < up.length(); i++) {
char ch = up.charAt(i);
if (ch != '_' && !Character.isLetterOrDigit(ch)) {
break;
}
}
if (i == 0) {
return s;
}
String alias = up.substring(0, i);
if ("SET".equals(alias) || Parser.isKeyword(alias, true)) {
return s;
}
if (newAlias) {
sentence.addAlias(alias, sentence.getLastTable());
}
HashMap<String, DbTableOrView> map = sentence.getAliases();
if ((map != null && map.containsKey(alias)) || (sentence.getLastTable() == null)) {
if (newAlias && s.length() == alias.length()) {
return s;
}
s = s.substring(alias.length());
if (s.length() == 0) {
sentence.add(alias + ".", ".", Sentence.CONTEXT);
}
return s;
}
HashSet<DbTableOrView> tables = sentence.getTables();
if (tables != null) {
String best = null;
for (DbTableOrView table : tables) {
String tableName = StringUtils.toUpperEnglish(table.getName());
if (alias.startsWith(tableName) && (best == null || tableName.length() > best.length())) {
sentence.setLastMatchedTable(table);
best = tableName;
} else if (s.length() == 0 || tableName.startsWith(alias)) {
sentence.add(tableName + ".", tableName.substring(s.length()) + ".", Sentence.CONTEXT);
}
}
if (best != null) {
s = s.substring(best.length());
if (s.length() == 0) {
sentence.add(alias + ".", ".", Sentence.CONTEXT);
}
return s;
}
}
return s;
}
}
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.bnf.context;
import org.h2.util.New;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* Contains meta data information about a procedure.
* This class is used by the H2 Console.
*/
public class DbProcedure {
private final DbSchema schema;
private final String name;
private final String quotedName;
private boolean returnsResult;
private DbColumn[] parameters;
public DbProcedure(DbSchema schema, ResultSet rs) throws SQLException {
this.schema = schema;
name = rs.getString("PROCEDURE_NAME");
returnsResult = rs.getShort("PROCEDURE_TYPE") == DatabaseMetaData.procedureReturnsResult;
quotedName = schema.getContents().quoteIdentifier(name);
}
/**
* @return The schema this table belongs to.
*/
public DbSchema getSchema() {
return schema;
}
/**
* @return The column list.
*/
public DbColumn[] getParameters() {
return parameters;
}
/**
* @return The table name.
*/
public String getName() {
return name;
}
/**
* @return The quoted table name.
*/
public String getQuotedName() {
return quotedName;
}
/**
* @return True if this function return a value
*/
public boolean isReturnsResult() {
return returnsResult;
}
/**
* Read the column for this table from the database meta data.
*
* @param meta the database meta data
*/
void readParameters(DatabaseMetaData meta) throws SQLException {
ResultSet rs = meta.getProcedureColumns(null, schema.name, name, null);
ArrayList<DbColumn> list = New.arrayList();
while (rs.next()) {
DbColumn column = new DbColumn(schema.getContents(), rs);
if(column.getPosition()>0) { //Not the return type
list.add(column);
}
}
rs.close();
parameters = new DbColumn[list.size()];
// Store the parameter in the good position [1-n]
for(int i=0;i<parameters.length;i++) {
DbColumn column = list.get(i);
if(column.getPosition()>0 && column.getPosition() <= parameters.length) {
parameters[column.getPosition() - 1] = column;
}
}
}
}
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.server.web; package org.h2.bnf.context;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
* Contains meta data information about a database schema. * Contains meta data information about a database schema.
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
public class DbSchema { public class DbSchema {
/** /**
* Up to this many tables, the column type and indexes are listed. * Up to this many tables, the column type and indexes are listed.
*/ */
static final int MAX_TABLES_LIST_INDEXES = 100; public static final int MAX_TABLES_LIST_INDEXES = 100;
/** /**
* Up to this many tables, the column names are listed. * Up to this many tables, the column names are listed.
*/ */
static final int MAX_TABLES_LIST_COLUMNS = 500; public static final int MAX_TABLES_LIST_COLUMNS = 500;
/** /**
* The database content container. * Up to this many tables, the column names are listed.
*/ */
final DbContents contents; public static final int MAX_PROCEDURES_LIST_COLUMNS = 500;
/** /**
* The schema name. * The database content container.
*/ */
final String name; private final DbContents contents;
/** /**
* True if this is the default schema for this database. * The schema name.
*/ */
final boolean isDefault; public final String name;
/** /**
* True if this is a system schema (for example the INFORMATION_SCHEMA). * True if this is the default schema for this database.
*/ */
final boolean isSystem; public final boolean isDefault;
/** /**
* The quoted schema name. * True if this is a system schema (for example the INFORMATION_SCHEMA).
*/ */
final String quotedName; public final boolean isSystem;
/** /**
* The table list. * The quoted schema name.
*/ */
DbTableOrView[] tables; public final String quotedName;
DbSchema(DbContents contents, String name, boolean isDefault) { /**
this.contents = contents; * The table list.
this.name = name; */
this.quotedName = contents.quoteIdentifier(name); private DbTableOrView[] tables;
this.isDefault = isDefault;
if (name.equals("INFORMATION_SCHEMA")) { /**
isSystem = true; * The procedures list.
} else if (!contents.isH2 && StringUtils.toUpperEnglish(name).startsWith("INFO")) { */
isSystem = true; private DbProcedure[] procedures;
} else if (contents.isPostgreSQL && StringUtils.toUpperEnglish(name).startsWith("PG_")) {
isSystem = true; DbSchema(DbContents contents, String name, boolean isDefault) {
} else if (contents.isDerby && name.startsWith("SYS")) { this.contents = contents;
isSystem = true; this.name = name;
} else { this.quotedName = contents.quoteIdentifier(name);
isSystem = false; this.isDefault = isDefault;
} if (name.equals("INFORMATION_SCHEMA")) {
} isSystem = true;
} else if (!contents.isH2() && StringUtils.toUpperEnglish(name).startsWith("INFO")) {
/** isSystem = true;
* Read all tables for this schema from the database meta data. } else if (contents.isPostgreSQL() && StringUtils.toUpperEnglish(name).startsWith("PG_")) {
* isSystem = true;
* @param meta the database meta data } else if (contents.isDerby() && name.startsWith("SYS")) {
* @param tableTypes the table types to read isSystem = true;
*/ } else {
void readTables(DatabaseMetaData meta, String[] tableTypes) throws SQLException { isSystem = false;
ResultSet rs = meta.getTables(null, name, null, tableTypes); }
ArrayList<DbTableOrView> list = New.arrayList(); }
while (rs.next()) {
DbTableOrView table = new DbTableOrView(this, rs); /**
if (contents.isOracle && table.name.indexOf('$') > 0) { * @return The database content container.
continue; */
} public DbContents getContents() {
list.add(table); return contents;
} }
rs.close();
tables = new DbTableOrView[list.size()]; /**
list.toArray(tables); * @return The table list.
if (tables.length < MAX_TABLES_LIST_COLUMNS) { */
for (DbTableOrView tab : tables) { public DbTableOrView[] getTables() {
tab.readColumns(meta); return tables;
} }
}
} /**
* @return The procedure list.
} */
public DbProcedure[] getProcedures() {
return procedures;
}
/**
* Read all tables for this schema from the database meta data.
*
* @param meta the database meta data
* @param tableTypes the table types to read
*/
public void readTables(DatabaseMetaData meta, String[] tableTypes) throws SQLException {
ResultSet rs = meta.getTables(null, name, null, tableTypes);
ArrayList<DbTableOrView> list = New.arrayList();
while (rs.next()) {
DbTableOrView table = new DbTableOrView(this, rs);
if (contents.isOracle() && table.getName().indexOf('$') > 0) {
continue;
}
list.add(table);
}
rs.close();
tables = new DbTableOrView[list.size()];
list.toArray(tables);
if (tables.length < MAX_TABLES_LIST_COLUMNS) {
for (DbTableOrView tab : tables) {
tab.readColumns(meta);
}
}
}
/**
* Read all procedures in the dataBase.
* @param meta the database meta data
* @throws SQLException Error while fetching procedures
*/
public void readProcedures(DatabaseMetaData meta) throws SQLException {
ResultSet rs = meta.getProcedures(null, name, null);
ArrayList<DbProcedure> list = New.arrayList();
while (rs.next()) {
list.add(new DbProcedure(this, rs));
}
rs.close();
procedures = new DbProcedure[list.size()];
list.toArray(procedures);
if (procedures.length < MAX_PROCEDURES_LIST_COLUMNS) {
for (DbProcedure procedure : procedures) {
procedure.readParameters(meta);
}
}
}
}
/* /*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0 * Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.server.web; package org.h2.bnf.context;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.util.New; import org.h2.util.New;
/** /**
* Contains meta data information about a table or a view. * Contains meta data information about a table or a view.
* This class is used by the H2 Console. * This class is used by the H2 Console.
*/ */
public class DbTableOrView { public class DbTableOrView {
/** /**
* The schema this table belongs to. * The schema this table belongs to.
*/ */
final DbSchema schema; private final DbSchema schema;
/** /**
* The table name. * The table name.
*/ */
final String name; private final String name;
/** /**
* The quoted table name. * The quoted table name.
*/ */
final String quotedName; private final String quotedName;
/** /**
* True if this represents a view. * True if this represents a view.
*/ */
final boolean isView; private final boolean isView;
/** /**
* The column list. * The column list.
*/ */
DbColumn[] columns; private DbColumn[] columns;
DbTableOrView(DbSchema schema, ResultSet rs) throws SQLException { public DbTableOrView(DbSchema schema, ResultSet rs) throws SQLException {
this.schema = schema; this.schema = schema;
name = rs.getString("TABLE_NAME"); name = rs.getString("TABLE_NAME");
String type = rs.getString("TABLE_TYPE"); String type = rs.getString("TABLE_TYPE");
isView = "VIEW".equals(type); isView = "VIEW".equals(type);
quotedName = schema.contents.quoteIdentifier(name); quotedName = schema.getContents().quoteIdentifier(name);
} }
/** /**
* Read the column for this table from the database meta data. * @return The schema this table belongs to.
* */
* @param meta the database meta data public DbSchema getSchema() {
*/ return schema;
void readColumns(DatabaseMetaData meta) throws SQLException { }
ResultSet rs = meta.getColumns(null, schema.name, name, null);
ArrayList<DbColumn> list = New.arrayList(); /**
while (rs.next()) { * @return The column list.
DbColumn column = new DbColumn(schema.contents, rs); */
list.add(column); public DbColumn[] getColumns() {
} return columns;
rs.close(); }
columns = new DbColumn[list.size()];
list.toArray(columns); /**
} * @return The table name.
*/
} public String getName() {
return name;
}
/**
* @return True if this represents a view.
*/
public boolean isView() {
return isView;
}
/**
* @return The quoted table name.
*/
public String getQuotedName() {
return quotedName;
}
/**
* Read the column for this table from the database meta data.
*
* @param meta the database meta data
*/
public void readColumns(DatabaseMetaData meta) throws SQLException {
ResultSet rs = meta.getColumns(null, schema.name, name, null);
ArrayList<DbColumn> list = New.arrayList();
while (rs.next()) {
DbColumn column = new DbColumn(schema.getContents(), rs);
list.add(column);
}
rs.close();
columns = new DbColumn[list.size()];
list.toArray(columns);
}
}
...@@ -167,7 +167,7 @@ CREATE DOMAIN [ IF NOT EXISTS ] newDomainName AS dataType ...@@ -167,7 +167,7 @@ CREATE DOMAIN [ IF NOT EXISTS ] newDomainName AS dataType
Creates a new data type (domain)." Creates a new data type (domain)."
"Commands (DDL)","CREATE INDEX"," "Commands (DDL)","CREATE INDEX","
CREATE CREATE
{ [ UNIQUE ] [ HASH ] INDEX [ [ IF NOT EXISTS ] newIndexName ] { [ UNIQUE ] [ HASH ] [ SPATIAL] INDEX [ [ IF NOT EXISTS ] newIndexName ]
| PRIMARY KEY [ HASH ] } | PRIMARY KEY [ HASH ] }
ON tableName ( indexColumn [,...] ) ON tableName ( indexColumn [,...] )
"," ","
...@@ -541,7 +541,7 @@ Default expressions are used if no explicit value was used when adding a row." ...@@ -541,7 +541,7 @@ Default expressions are used if no explicit value was used when adding a row."
"," ","
Comments can be used anywhere in a command and are ignored by the database." Comments can be used anywhere in a command and are ignored by the database."
"Other Grammar","Compare"," "Other Grammar","Compare","
<> | <= | >= | = | < | > | != <> | <= | >= | = | < | > | != | &&
"," ","
Comparison operator." Comparison operator."
"Other Grammar","Condition"," "Other Grammar","Condition","
......
...@@ -34,6 +34,10 @@ import java.util.Map; ...@@ -34,6 +34,10 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Random; import java.util.Random;
import org.h2.bnf.Bnf; import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbColumn;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbSchema;
import org.h2.bnf.context.DbTableOrView;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
...@@ -469,20 +473,20 @@ public class WebApp { ...@@ -469,20 +473,20 @@ public class WebApp {
private static int addColumns(boolean mainSchema, DbTableOrView table, private static int addColumns(boolean mainSchema, DbTableOrView table,
StringBuilder buff, int treeIndex, boolean showColumnTypes, StringBuilder buff, int treeIndex, boolean showColumnTypes,
StringBuilder columnsBuffer) { StringBuilder columnsBuffer) {
DbColumn[] columns = table.columns; DbColumn[] columns = table.getColumns();
for (int i = 0; columns != null && i < columns.length; i++) { for (int i = 0; columns != null && i < columns.length; i++) {
DbColumn column = columns[i]; DbColumn column = columns[i];
if (columnsBuffer.length() > 0) { if (columnsBuffer.length() > 0) {
columnsBuffer.append(' '); columnsBuffer.append(' ');
} }
columnsBuffer.append(column.name); columnsBuffer.append(column.getName());
String col = escapeIdentifier(column.name); String col = escapeIdentifier(column.getName());
String level = mainSchema ? ", 1, 1" : ", 2, 2"; String level = mainSchema ? ", 1, 1" : ", 2, 2";
buff.append("setNode(" + treeIndex + level + ", 'column', '" + PageParser.escapeJavaScript(column.name) buff.append("setNode(" + treeIndex + level + ", 'column', '" + PageParser.escapeJavaScript(column.getName())
+ "', 'javascript:ins(\\'" + col + "\\')');\n"); + "', 'javascript:ins(\\'" + col + "\\')');\n");
treeIndex++; treeIndex++;
if (mainSchema && showColumnTypes) { if (mainSchema && showColumnTypes) {
buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '" + PageParser.escapeJavaScript(column.dataType) buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '" + PageParser.escapeJavaScript(column.getDataType())
+ "', null);\n"); + "', null);\n");
treeIndex++; treeIndex++;
} }
...@@ -585,57 +589,57 @@ public class WebApp { ...@@ -585,57 +589,57 @@ public class WebApp {
boolean showColumns = mainSchema || !schema.isSystem; boolean showColumns = mainSchema || !schema.isSystem;
String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", "; String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", ";
String indentNode = ", " + (level + 1) + ", 2, "; String indentNode = ", " + (level + 1) + ", 2, ";
DbTableOrView[] tables = schema.tables; DbTableOrView[] tables = schema.getTables();
if (tables == null) { if (tables == null) {
return treeIndex; return treeIndex;
} }
boolean isOracle = schema.contents.isOracle; boolean isOracle = schema.getContents().isOracle();
boolean notManyTables = tables.length < DbSchema.MAX_TABLES_LIST_INDEXES; boolean notManyTables = tables.length < DbSchema.MAX_TABLES_LIST_INDEXES;
for (DbTableOrView table : tables) { for (DbTableOrView table : tables) {
if (table.isView) { if (table.isView()) {
continue; continue;
} }
int tableId = treeIndex; int tableId = treeIndex;
String tab = table.quotedName; String tab = table.getQuotedName();
if (!mainSchema) { if (!mainSchema) {
tab = schema.quotedName + "." + tab; tab = schema.quotedName + "." + tab;
} }
tab = escapeIdentifier(tab); tab = escapeIdentifier(tab);
buff.append("setNode(" + treeIndex + indentation + " 'table', '" + PageParser.escapeJavaScript(table.name) buff.append("setNode(" + treeIndex + indentation + " 'table', '" + PageParser.escapeJavaScript(table.getName())
+ "', 'javascript:ins(\\'" + tab + "\\',true)');\n"); + "', 'javascript:ins(\\'" + tab + "\\',true)');\n");
treeIndex++; treeIndex++;
if (mainSchema || showColumns) { if (mainSchema || showColumns) {
StringBuilder columnsBuffer = new StringBuilder(); StringBuilder columnsBuffer = new StringBuilder();
treeIndex = addColumns(mainSchema, table, buff, treeIndex, notManyTables, columnsBuffer); treeIndex = addColumns(mainSchema, table, buff, treeIndex, notManyTables, columnsBuffer);
if (!isOracle && notManyTables) { if (!isOracle && notManyTables) {
treeIndex = addIndexes(mainSchema, meta, table.name, schema.name, buff, treeIndex); treeIndex = addIndexes(mainSchema, meta, table.getName(), schema.name, buff, treeIndex);
} }
buff.append("addTable('" + PageParser.escapeJavaScript(table.name) + "', '" buff.append("addTable('" + PageParser.escapeJavaScript(table.getName()) + "', '"
+ PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n"); + PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n");
} }
} }
tables = schema.tables; tables = schema.getTables();
for (DbTableOrView view : tables) { for (DbTableOrView view : tables) {
if (!view.isView) { if (!view.isView()) {
continue; continue;
} }
int tableId = treeIndex; int tableId = treeIndex;
String tab = view.quotedName; String tab = view.getQuotedName();
if (!mainSchema) { if (!mainSchema) {
tab = view.schema.quotedName + "." + tab; tab = view.getSchema().quotedName + "." + tab;
} }
tab = escapeIdentifier(tab); tab = escapeIdentifier(tab);
buff.append("setNode(" + treeIndex + indentation + " 'view', '" + PageParser.escapeJavaScript(view.name) buff.append("setNode(" + treeIndex + indentation + " 'view', '" + PageParser.escapeJavaScript(view.getName())
+ "', 'javascript:ins(\\'" + tab + "\\',true)');\n"); + "', 'javascript:ins(\\'" + tab + "\\',true)');\n");
treeIndex++; treeIndex++;
if (mainSchema) { if (mainSchema) {
StringBuilder columnsBuffer = new StringBuilder(); StringBuilder columnsBuffer = new StringBuilder();
treeIndex = addColumns(mainSchema, view, buff, treeIndex, notManyTables, columnsBuffer); treeIndex = addColumns(mainSchema, view, buff, treeIndex, notManyTables, columnsBuffer);
if (schema.contents.isH2) { if (schema.getContents().isH2()) {
PreparedStatement prep = null; PreparedStatement prep = null;
try { try {
prep = conn.prepareStatement("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?"); prep = conn.prepareStatement("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?");
prep.setString(1, view.name); prep.setString(1, view.getName());
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
if (rs.next()) { if (rs.next()) {
String sql = rs.getString("SQL"); String sql = rs.getString("SQL");
...@@ -648,7 +652,7 @@ public class WebApp { ...@@ -648,7 +652,7 @@ public class WebApp {
JdbcUtils.closeSilently(prep); JdbcUtils.closeSilently(prep);
} }
} }
buff.append("addTable('" + PageParser.escapeJavaScript(view.name) + "', '" buff.append("addTable('" + PageParser.escapeJavaScript(view.getName()) + "', '"
+ PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n"); + PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n");
} }
} }
...@@ -663,16 +667,16 @@ public class WebApp { ...@@ -663,16 +667,16 @@ public class WebApp {
session.loadBnf(); session.loadBnf();
Connection conn = session.getConnection(); Connection conn = session.getConnection();
DatabaseMetaData meta = session.getMetaData(); DatabaseMetaData meta = session.getMetaData();
isH2 = contents.isH2; isH2 = contents.isH2();
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append("setNode(0, 0, 0, 'database', '" + PageParser.escapeJavaScript((String) session.get("url")) buff.append("setNode(0, 0, 0, 'database', '" + PageParser.escapeJavaScript((String) session.get("url"))
+ "', null);\n"); + "', null);\n");
int treeIndex = 1; int treeIndex = 1;
DbSchema defaultSchema = contents.defaultSchema; DbSchema defaultSchema = contents.getDefaultSchema();
treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex); treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex);
DbSchema[] schemas = contents.schemas; DbSchema[] schemas = contents.getSchemas();
for (DbSchema schema : schemas) { for (DbSchema schema : schemas) {
if (schema == defaultSchema || schema == null) { if (schema == defaultSchema || schema == null) {
continue; continue;
...@@ -983,7 +987,7 @@ public class WebApp { ...@@ -983,7 +987,7 @@ public class WebApp {
result = buff.toString(); result = buff.toString();
session.put("result", result); session.put("result", result);
} catch (Throwable e) { } catch (Throwable e) {
session.put("result", getStackTrace(0, e, session.getContents().isH2)); session.put("result", getStackTrace(0, e, session.getContents().isH2()));
} }
return "result.jsp"; return "result.jsp";
} }
...@@ -1034,7 +1038,7 @@ public class WebApp { ...@@ -1034,7 +1038,7 @@ public class WebApp {
// cancel // cancel
} }
} catch (Throwable e) { } catch (Throwable e) {
result = "<br />" + getStackTrace(0, e, session.getContents().isH2); result = "<br />" + getStackTrace(0, e, session.getContents().isH2());
error = formatAsError(e.getMessage()); error = formatAsError(e.getMessage());
} }
String sql = "@edit " + (String) session.get("resultSetSQL"); String sql = "@edit " + (String) session.get("resultSetSQL");
...@@ -1212,7 +1216,7 @@ public class WebApp { ...@@ -1212,7 +1216,7 @@ public class WebApp {
} }
Statement stat; Statement stat;
DbContents contents = session.getContents(); DbContents contents = session.getContents();
if (forceEdit || (allowEdit && contents.isH2)) { if (forceEdit || (allowEdit && contents.isH2())) {
stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
} else { } else {
stat = conn.createStatement(); stat = conn.createStatement();
...@@ -1342,7 +1346,7 @@ public class WebApp { ...@@ -1342,7 +1346,7 @@ public class WebApp {
return buff.toString(); return buff.toString();
} catch (Throwable e) { } catch (Throwable e) {
// throwable: including OutOfMemoryError and so on // throwable: including OutOfMemoryError and so on
return getStackTrace(id, e, session.getContents().isH2); return getStackTrace(id, e, session.getContents().isH2());
} finally { } finally {
session.executingStatement = null; session.executingStatement = null;
} }
...@@ -1405,7 +1409,7 @@ public class WebApp { ...@@ -1405,7 +1409,7 @@ public class WebApp {
prep.setInt(j + 1, i); prep.setInt(j + 1, i);
} }
} }
if (session.getContents().isSQLite) { if (session.getContents().isSQLite()) {
// SQLite currently throws an exception on prep.execute() // SQLite currently throws an exception on prep.execute()
prep.executeUpdate(); prep.executeUpdate();
} else { } else {
...@@ -1746,7 +1750,7 @@ public class WebApp { ...@@ -1746,7 +1750,7 @@ public class WebApp {
} }
ResultSetMetaData meta = rs.getMetaData(); ResultSetMetaData meta = rs.getMetaData();
int type = meta.getColumnType(columnIndex); int type = meta.getColumnType(columnIndex);
if (session.getContents().isH2) { if (session.getContents().isH2()) {
rs.updateString(columnIndex, x); rs.updateString(columnIndex, x);
return; return;
} }
......
...@@ -17,6 +17,8 @@ import java.util.HashMap; ...@@ -17,6 +17,8 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import org.h2.bnf.Bnf; import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbContextRule;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.util.New; import org.h2.util.New;
...@@ -120,12 +122,14 @@ class WebSession { ...@@ -120,12 +122,14 @@ class WebSession {
DbContextRule tableRule = new DbContextRule(contents, DbContextRule.TABLE); DbContextRule tableRule = new DbContextRule(contents, DbContextRule.TABLE);
DbContextRule schemaRule = new DbContextRule(contents, DbContextRule.SCHEMA); DbContextRule schemaRule = new DbContextRule(contents, DbContextRule.SCHEMA);
DbContextRule columnAliasRule = new DbContextRule(contents, DbContextRule.COLUMN_ALIAS); DbContextRule columnAliasRule = new DbContextRule(contents, DbContextRule.COLUMN_ALIAS);
DbContextRule procedureRule = new DbContextRule(contents, DbContextRule.PROCEDURE);
newBnf.updateTopic("column_name", columnRule); newBnf.updateTopic("column_name", columnRule);
newBnf.updateTopic("new_table_alias", newAliasRule); newBnf.updateTopic("new_table_alias", newAliasRule);
newBnf.updateTopic("table_alias", aliasRule); newBnf.updateTopic("table_alias", aliasRule);
newBnf.updateTopic("column_alias", columnAliasRule); newBnf.updateTopic("column_alias", columnAliasRule);
newBnf.updateTopic("table_name", tableRule); newBnf.updateTopic("table_name", tableRule);
newBnf.updateTopic("schema_name", schemaRule); newBnf.updateTopic("schema_name", schemaRule);
newBnf.updateTopic("expression", procedureRule);
newBnf.linkStatements(); newBnf.linkStatements();
bnf = newBnf; bnf = newBnf;
} catch (Exception e) { } catch (Exception e) {
......
...@@ -144,6 +144,7 @@ import org.h2.test.synth.sql.TestSynth; ...@@ -144,6 +144,7 @@ import org.h2.test.synth.sql.TestSynth;
import org.h2.test.synth.thread.TestMulti; import org.h2.test.synth.thread.TestMulti;
import org.h2.test.unit.TestAutoReconnect; import org.h2.test.unit.TestAutoReconnect;
import org.h2.test.unit.TestBitField; import org.h2.test.unit.TestBitField;
import org.h2.test.unit.TestBnf;
import org.h2.test.unit.TestCache; import org.h2.test.unit.TestCache;
import org.h2.test.unit.TestClearReferences; import org.h2.test.unit.TestClearReferences;
import org.h2.test.unit.TestCollation; import org.h2.test.unit.TestCollation;
...@@ -608,6 +609,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -608,6 +609,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestAlterSchemaRename().runTest(this); new TestAlterSchemaRename().runTest(this);
new TestAutoRecompile().runTest(this); new TestAutoRecompile().runTest(this);
new TestBitField().runTest(this); new TestBitField().runTest(this);
new TestBnf().runTest(this);
new TestBackup().runTest(this); new TestBackup().runTest(this);
new TestBigDb().runTest(this); new TestBigDb().runTest(this);
new TestBigResult().runTest(this); new TestBigResult().runTest(this);
......
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbContextRule;
import org.h2.bnf.context.DbProcedure;
import org.h2.bnf.context.DbSchema;
import org.h2.test.TestBase;
import java.sql.Connection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Test Bnf Sql parser
* @author Nicolas Fortin
*/
public class TestBnf extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
deleteDb("TestBnf");
Connection conn = getConnection("TestBnf");
try {
testProcedures(conn);
} finally {
conn.close();
}
}
private void testProcedures(Connection conn) throws Exception {
// Register a procedure and check if it is present in DbContents
conn.createStatement().execute("DROP ALIAS IF EXISTS CUSTOM_PRINT");
conn.createStatement().execute("CREATE ALIAS CUSTOM_PRINT AS $$ void print(String s) { System.out.println(s); } $$");
conn.createStatement().execute("DROP TABLE IF EXISTS TABLE_WITH_STRING_FIELD");
conn.createStatement().execute("CREATE TABLE TABLE_WITH_STRING_FIELD (STRING_FIELD VARCHAR(50), INT_FIELD integer)");
DbContents dbContents = new DbContents();
dbContents.readContents(conn.getMetaData());
DbSchema defaultSchema = dbContents.getDefaultSchema();
DbProcedure[] procedures = defaultSchema.getProcedures();
Set<String> procedureName = new HashSet<String>(procedures.length);
for(DbProcedure procedure : procedures) {
procedureName.add(procedure.getName());
}
assertTrue(procedureName.contains("CUSTOM_PRINT"));
// Test completion
Bnf bnf = Bnf.getInstance(null);
DbContextRule columnRule = new DbContextRule(dbContents, DbContextRule.COLUMN);
bnf.updateTopic("column_name", columnRule);
bnf.updateTopic("expression", new DbContextRule(dbContents, DbContextRule.PROCEDURE));
bnf.linkStatements();
// Test partial
Map<String, String> tokens = bnf.getNextTokenList("SELECT CUSTOM_PR");
assertTrue(tokens.values().contains("INT"));
// Test parameters
tokens = bnf.getNextTokenList("SELECT CUSTOM_PRINT(");
assertTrue(tokens.values().contains("STRING_FIELD"));
assertFalse(tokens.values().contains("INT_FIELD"));
// Test parameters with spaces
tokens = bnf.getNextTokenList("SELECT CUSTOM_PRINT ( ");
assertTrue(tokens.values().contains("STRING_FIELD"));
assertFalse(tokens.values().contains("INT_FIELD"));
// Test parameters with close bracket
tokens = bnf.getNextTokenList("SELECT CUSTOM_PRINT ( STRING_FIELD");
assertTrue(tokens.values().contains(")"));
}
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论