001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.sql;
020:
021: import java.sql.Connection;
022: import java.sql.DatabaseMetaData;
023: import java.sql.SQLException;
024: import java.sql.Types;
025:
026: import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
027: import org.apache.openjpa.kernel.Filters;
028: import org.apache.openjpa.jdbc.schema.Column;
029: import org.apache.openjpa.lib.util.Localizer;
030: import org.apache.openjpa.meta.JavaTypes;
031:
032: /**
033: * Dictionary for MS SQLServer.
034: */
035: public class SQLServerDictionary extends AbstractSQLServerDictionary {
036:
037: public static final String VENDOR_MICROSOFT = "microsoft";
038: public static final String VENDOR_NETDIRECT = "netdirect";
039: public static final String VENDOR_JTDS = "jtds";
040:
041: private static final Localizer _loc = Localizer
042: .forPackage(SQLServerDictionary.class);
043:
044: private String schemaCase = SCHEMA_CASE_PRESERVE;
045: /**
046: * Flag whether to treat UNIQUEIDENTIFIER as VARBINARY or VARCHAR
047: */
048: public boolean uniqueIdentifierAsVarbinary = true;
049:
050: public SQLServerDictionary() {
051: platform = "Microsoft SQL Server";
052:
053: // SQLServer locks on a table-by-table basis
054: forUpdateClause = null;
055: tableForUpdateClause = "WITH (UPDLOCK)";
056:
057: supportsNullTableForGetColumns = false;
058: requiresAliasForSubselect = true;
059:
060: stringLengthFunction = "LEN({0})";
061: }
062:
063: public void connectedConfiguration(Connection conn)
064: throws SQLException {
065: super .connectedConfiguration(conn);
066:
067: DatabaseMetaData meta = conn.getMetaData();
068: String driverName = meta.getDriverName();
069: String url = meta.getURL();
070: if (driverVendor == null) {
071: if ("NetDirect JSQLConnect".equals(driverName))
072: driverVendor = VENDOR_NETDIRECT;
073: else if (driverName != null
074: && driverName.startsWith("jTDS"))
075: driverVendor = VENDOR_JTDS;
076: else if ("SQLServer".equals(driverName)) {
077: if (url != null
078: && url.startsWith("jdbc:microsoft:sqlserver:"))
079: driverVendor = VENDOR_MICROSOFT;
080: else if (url != null
081: && url.startsWith("jdbc:datadirect:sqlserver:"))
082: driverVendor = VENDOR_DATADIRECT;
083: else
084: driverVendor = VENDOR_OTHER;
085: } else
086: driverVendor = VENDOR_OTHER;
087: if (driverName.indexOf(platform) != -1) {
088: String versionString = driverName.substring(platform
089: .length() + 1);
090: if (versionString.indexOf(" ") != -1)
091: versionString = versionString.substring(0,
092: versionString.indexOf(" "));
093: int version = Integer.parseInt(versionString);
094: if (version >= 2005)
095: supportsXMLColumn = true;
096: }
097: }
098:
099: // warn about using cursors
100: if ((VENDOR_MICROSOFT.equalsIgnoreCase(driverVendor) || VENDOR_DATADIRECT
101: .equalsIgnoreCase(driverVendor))
102: && url.toLowerCase().indexOf("selectmethod=cursor") == -1)
103: log.warn(_loc.get("sqlserver-cursor", url));
104:
105: // warn about prepared statement caching if using ms driver
106: String props = conf.getConnectionFactoryProperties();
107: if (props == null)
108: props = "";
109: if (VENDOR_MICROSOFT.equalsIgnoreCase(driverVendor)
110: && props.toLowerCase().indexOf("maxcachedstatements=0") == -1)
111: log.warn(_loc.get("sqlserver-cachedstmnts"));
112: }
113:
114: public Column[] getColumns(DatabaseMetaData meta, String catalog,
115: String schemaName, String tableName, String columnName,
116: Connection conn) throws SQLException {
117: Column[] cols = super .getColumns(meta, catalog, schemaName,
118: tableName, columnName, conn);
119:
120: // for opta driver, which reports nvarchar as unknown type
121: for (int i = 0; cols != null && i < cols.length; i++) {
122: String typeName = cols[i].getTypeName();
123: if (typeName == null)
124: continue;
125:
126: typeName = typeName.toUpperCase();
127:
128: if ("NVARCHAR".equals(typeName))
129: cols[i].setType(Types.VARCHAR);
130: else if ("UNIQUEIDENTIFIER".equals(typeName)) {
131: if (uniqueIdentifierAsVarbinary)
132: cols[i].setType(Types.VARBINARY);
133: else
134: cols[i].setType(Types.VARCHAR);
135: } else if ("NCHAR".equals(typeName))
136: cols[i].setType(Types.CHAR);
137: else if ("NTEXT".equals(typeName))
138: cols[i].setType(Types.CLOB);
139: }
140: return cols;
141: }
142:
143: protected void appendLength(SQLBuffer buf, int type) {
144: if (type == Types.VARCHAR)
145: buf.append("(").append(
146: Integer.toString(characterColumnSize)).append(")");
147: }
148:
149: /**
150: * If this dictionary supports XML type,
151: * use this method to append xml predicate.
152: *
153: * @param buf the SQL buffer to write the comparison
154: * @param op the comparison operation to perform
155: * @param lhs the left hand side of the comparison
156: * @param rhs the right hand side of the comparison
157: * @param lhsxml indicates whether the left operand maps to xml
158: * @param rhsxml indicates whether the right operand maps to xml
159: */
160: public void appendXmlComparison(SQLBuffer buf, String op,
161: FilterValue lhs, FilterValue rhs, boolean lhsxml,
162: boolean rhsxml) {
163: super .appendXmlComparison(buf, op, lhs, rhs, lhsxml, rhsxml);
164: if (lhsxml && rhsxml)
165: appendXmlComparison2(buf, op, lhs, rhs);
166: else if (lhsxml)
167: appendXmlComparison1(buf, op, lhs, rhs);
168: else
169: appendXmlComparison1(buf, op, rhs, lhs);
170: }
171:
172: /**
173: * Append an xml comparison predicate
174: *
175: * @param buf the SQL buffer to write the comparison
176: * @param op the comparison operation to perform
177: * @param lhs the left hand side of the comparison (maps to xml column)
178: * @param rhs the right hand side of the comparison
179: */
180: private void appendXmlComparison1(SQLBuffer buf, String op,
181: FilterValue lhs, FilterValue rhs) {
182: boolean castrhs = rhs.isConstant();
183: if (castrhs)
184: appendXmlValue(buf, lhs);
185: else
186: appendXmlExist(buf, lhs);
187: buf.append(" ").append(op).append(" ");
188: if (castrhs)
189: rhs.appendTo(buf);
190: else {
191: buf.append("sql:column(\"");
192: rhs.appendTo(buf);
193: buf.append("\")").append("]') = 1");
194: }
195: }
196:
197: private void appendXmlExist(SQLBuffer buf, FilterValue lhs) {
198: buf.append(
199: lhs
200: .getColumnAlias(lhs.getFieldMapping()
201: .getColumns()[0])).append(".exist('")
202: .append("/*[");
203: lhs.appendTo(buf);
204: }
205:
206: /**
207: * Append an xml comparison predicate (both operands map to xml column)
208: *
209: * @param buf the SQL buffer to write the comparison
210: * @param op the comparison operation to perform
211: * @param lhs the left hand side of the comparison (maps to xml column)
212: * @param rhs the right hand side of the comparison (maps to xml column)
213: */
214: private void appendXmlComparison2(SQLBuffer buf, String op,
215: FilterValue lhs, FilterValue rhs) {
216: appendXmlValue(buf, lhs);
217: buf.append(" ").append(op).append(" ");
218: appendXmlValue(buf, rhs);
219: }
220:
221: private void appendXmlValue(SQLBuffer buf, FilterValue val) {
222: Class rc = Filters.wrap(val.getType());
223: int type = getJDBCType(JavaTypes.getTypeCode(rc), false);
224: boolean isXmlAttribute = (val.getXmlMapping() == null) ? false
225: : val.getXmlMapping().isXmlAttribute();
226: buf.append(
227: val
228: .getColumnAlias(val.getFieldMapping()
229: .getColumns()[0])).append(".value(")
230: .append("'(/*/");
231: val.appendTo(buf);
232: if (!isXmlAttribute)
233: buf.append("/text()");
234: buf.append(")[1]','").append(getTypeName(type));
235: appendLength(buf, type);
236: buf.append("')");
237: }
238:
239: /**
240: * Return DB specific schemaCase
241: */
242: public String getSchemaCase() {
243: return schemaCase;
244: }
245: }
|