/* * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification import io.opentelemetry.semconv.trace.attributes.SemanticAttributes import org.hibernate.Session import org.hibernate.SessionFactory import org.hibernate.cfg.Configuration import org.hibernate.exception.SQLGrammarException import org.hibernate.procedure.ProcedureCall import spock.lang.Shared import javax.persistence.ParameterMode import java.sql.Connection import java.sql.DriverManager import java.sql.Statement import static io.opentelemetry.api.trace.SpanKind.CLIENT import static io.opentelemetry.api.trace.SpanKind.INTERNAL import static io.opentelemetry.api.trace.StatusCode.ERROR class ProcedureCallTest extends AgentInstrumentationSpecification { @Shared protected SessionFactory sessionFactory @Shared protected List prepopulated def setupSpec() { sessionFactory = new Configuration().configure().buildSessionFactory() // Pre-populate the DB, so delete/update can be tested. Session writer = sessionFactory.openSession() writer.beginTransaction() prepopulated = new ArrayList<>() for (int i = 0; i < 2; i++) { prepopulated.add(new Value("Hello :) " + i)) writer.save(prepopulated.get(i)) } writer.getTransaction().commit() writer.close() // Create a stored procedure. Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", "1") Statement stmt = conn.createStatement() stmt.execute("CREATE PROCEDURE TEST_PROC() MODIFIES SQL DATA BEGIN ATOMIC INSERT INTO Value VALUES (420, 'fred'); END") stmt.close() conn.close() } def cleanupSpec() { if (sessionFactory != null) { sessionFactory.close() } } def "test ProcedureCall"() { setup: runWithSpan("parent") { Session session = sessionFactory.openSession() session.beginTransaction() ProcedureCall call = session.createStoredProcedureCall("TEST_PROC") call.getOutputs() session.getTransaction().commit() session.close() } expect: def sessionId assertTraces(1) { trace(0, 4) { span(0) { name "parent" kind INTERNAL hasNoParent() attributes { } } span(1) { name "ProcedureCall.getOutputs TEST_PROC" kind INTERNAL childOf span(0) attributes { "hibernate.session_id" { sessionId = it it instanceof String } } } span(2) { name "CALL test.TEST_PROC" kind CLIENT childOf span(1) attributes { "$SemanticAttributes.DB_SYSTEM" "hsqldb" "$SemanticAttributes.DB_NAME" "test" "$SemanticAttributes.DB_USER" "sa" "$SemanticAttributes.DB_STATEMENT" "{call TEST_PROC()}" "$SemanticAttributes.DB_CONNECTION_STRING" "hsqldb:mem:" "$SemanticAttributes.DB_OPERATION" "CALL" } } span(3) { kind INTERNAL name "Transaction.commit" childOf span(0) attributes { "hibernate.session_id" sessionId } } } } } def "test failing ProcedureCall"() { setup: runWithSpan("parent") { Session session = sessionFactory.openSession() session.beginTransaction() ProcedureCall call = session.createStoredProcedureCall("TEST_PROC") def parameterRegistration = call.registerParameter("nonexistent", Long, ParameterMode.IN) parameterRegistration.bindValue(420L) try { call.getOutputs() } catch (Exception e) { // We expected this. } session.getTransaction().commit() session.close() } expect: def sessionId assertTraces(1) { trace(0, 3) { span(0) { name "parent" kind INTERNAL hasNoParent() attributes { } } span(1) { name "ProcedureCall.getOutputs TEST_PROC" kind INTERNAL childOf span(0) status ERROR errorEvent(SQLGrammarException, "could not prepare statement") attributes { "hibernate.session_id" { sessionId = it it instanceof String } } } span(2) { name "Transaction.commit" kind INTERNAL childOf span(0) attributes { "hibernate.session_id" sessionId } } } } } }