001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.util; 021 022import ca.uhn.fhir.interceptor.model.RequestPartitionId; 023import ca.uhn.fhir.util.UrlUtil; 024import org.apache.commons.lang3.Validate; 025import org.hibernate.engine.jdbc.internal.BasicFormatterImpl; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029import java.util.ArrayList; 030import java.util.Collections; 031import java.util.List; 032 033import static org.apache.commons.lang3.StringUtils.trim; 034 035public class SqlQuery { 036 private static final Logger ourLog = LoggerFactory.getLogger(SqlQuery.class); 037 private final String myThreadName = Thread.currentThread().getName(); 038 private final String mySql; 039 private final List<String> myParams; 040 private final long myQueryTimestamp; 041 private final long myElapsedTime; 042 private final StackTraceElement[] myStackTrace; 043 private final int mySize; 044 private final LanguageEnum myLanguage; 045 private final String myNamespace; 046 private final RequestPartitionId myRequestPartitionId; 047 048 public SqlQuery( 049 String theSql, 050 List<String> theParams, 051 long theQueryTimestamp, 052 long theElapsedTime, 053 StackTraceElement[] theStackTraceElements, 054 int theSize, 055 RequestPartitionId theRequestPartitionId) { 056 this( 057 null, 058 theSql, 059 theParams, 060 theQueryTimestamp, 061 theElapsedTime, 062 theStackTraceElements, 063 theSize, 064 LanguageEnum.SQL, 065 theRequestPartitionId); 066 } 067 068 public SqlQuery( 069 String theNamespace, 070 String theSql, 071 List<String> theParams, 072 long theQueryTimestamp, 073 long theElapsedTime, 074 StackTraceElement[] theStackTraceElements, 075 int theSize, 076 LanguageEnum theLanguage, 077 RequestPartitionId theRequestPartitionId) { 078 Validate.notNull(theLanguage, "theLanguage must not be null"); 079 080 myNamespace = theNamespace; 081 mySql = theSql; 082 myParams = Collections.unmodifiableList(theParams); 083 myQueryTimestamp = theQueryTimestamp; 084 myElapsedTime = theElapsedTime; 085 myStackTrace = theStackTraceElements; 086 mySize = theSize; 087 myLanguage = theLanguage; 088 myRequestPartitionId = theRequestPartitionId; 089 } 090 091 public RequestPartitionId getRequestPartitionId() { 092 return myRequestPartitionId; 093 } 094 095 public String getNamespace() { 096 return myNamespace; 097 } 098 099 public long getQueryTimestamp() { 100 return myQueryTimestamp; 101 } 102 103 public long getElapsedTime() { 104 return myElapsedTime; 105 } 106 107 public String getThreadName() { 108 return myThreadName; 109 } 110 111 public String getSql(boolean theInlineParams, boolean theFormat) { 112 return getSql(theInlineParams, theFormat, false); 113 } 114 115 public LanguageEnum getLanguage() { 116 return myLanguage; 117 } 118 119 public String getSql(boolean theInlineParams, boolean theFormat, boolean theSanitizeParams) { 120 String retVal = mySql; 121 if (theFormat) { 122 if (getLanguage() == LanguageEnum.SQL) { 123 retVal = new BasicFormatterImpl().format(retVal); 124 125 // BasicFormatterImpl annoyingly adds a newline at the very start of its output 126 while (retVal.startsWith("\n")) { 127 retVal = retVal.substring(1); 128 } 129 } 130 } 131 132 if (theInlineParams) { 133 List<String> nextParams = new ArrayList<>(myParams); 134 int idx = 0; 135 while (nextParams.size() > 0) { 136 idx = retVal.indexOf("?", idx); 137 if (idx == -1) { 138 break; 139 } 140 String nextParamValue = nextParams.remove(0); 141 String nextSubstitution; 142 if (nextParamValue != null) { 143 if (theSanitizeParams) { 144 nextParamValue = UrlUtil.sanitizeUrlPart(nextParamValue); 145 } 146 nextSubstitution = "'" + nextParamValue + "'"; 147 } else { 148 nextSubstitution = "NULL"; 149 } 150 retVal = retVal.substring(0, idx) + nextSubstitution + retVal.substring(idx + 1); 151 idx += nextSubstitution.length(); 152 } 153 } 154 155 return trim(retVal); 156 } 157 158 public StackTraceElement[] getStackTrace() { 159 return myStackTrace; 160 } 161 162 public int getSize() { 163 return mySize; 164 } 165 166 @Override 167 public String toString() { 168 return getSql(true, true); 169 } 170 171 public enum LanguageEnum { 172 SQL, 173 JSON 174 } 175}