-
Notifications
You must be signed in to change notification settings - Fork 15
Add CREATE JOB DDL #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add CREATE JOB DDL #215
Changes from all commits
6c5f516
51a6e37
0ddf2b6
c79be73
3e0f3d9
6e9a8d6
45e9ad1
bdf0589
d02a019
a4afdec
cfca764
d439a97
a8fc4b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package com.linkedin.hoptimator; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
|
|
||
| /** Represents a CREATE JOB request for deploying a SqlJob to Kubernetes. */ | ||
| public class SqlJobDeployable implements Deployable { | ||
|
|
||
| private final String name; | ||
| private final String dialect; | ||
| private final String executionMode; | ||
| private final List<String> sql; | ||
| private final Map<String, String> options; | ||
|
|
||
| public SqlJobDeployable(String name, String dialect, String executionMode, | ||
| List<String> sql, Map<String, String> options) { | ||
| this.name = name; | ||
| this.dialect = dialect; | ||
| this.executionMode = executionMode; | ||
| this.sql = sql; | ||
| this.options = options; | ||
| } | ||
|
|
||
| public String name() { | ||
| return name; | ||
| } | ||
|
|
||
| /** Dialect, e.g. "Flink", "FlinkBeam". May be null for default. */ | ||
| public String dialect() { | ||
| return dialect; | ||
| } | ||
|
|
||
| /** Execution mode, e.g. "Streaming", "Batch". May be null for default. */ | ||
| public String executionMode() { | ||
| return executionMode; | ||
| } | ||
|
|
||
| public List<String> sql() { | ||
| return sql; | ||
| } | ||
|
|
||
| public Map<String, String> options() { | ||
| return options; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "SqlJob[" + name + "]"; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,9 +24,11 @@ | |
| import com.linkedin.hoptimator.DatabaseDeployable; | ||
| import com.linkedin.hoptimator.Deployer; | ||
| import com.linkedin.hoptimator.MaterializedView; | ||
| import com.linkedin.hoptimator.SqlJobDeployable; | ||
| import com.linkedin.hoptimator.Pipeline; | ||
| import com.linkedin.hoptimator.Source; | ||
| import com.linkedin.hoptimator.jdbc.ddl.SqlCreateDatabase; | ||
| import com.linkedin.hoptimator.jdbc.ddl.SqlCreateJob; | ||
| import com.linkedin.hoptimator.jdbc.ddl.SqlCreateMaterializedView; | ||
| import com.linkedin.hoptimator.jdbc.ddl.SqlCreateTable; | ||
| import com.linkedin.hoptimator.util.DeploymentService; | ||
|
|
@@ -672,6 +674,69 @@ static SpecifyResult processCreateDatabase(HoptimatorConnection conn, | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Shared implementation of the {@code CREATE JOB} pipeline for both real deployment | ||
| * and dry-run (SPECIFY) modes. | ||
| * | ||
| * @param conn the JDBC connection | ||
| * @param create the parsed DDL node | ||
| * @param mode whether to CREATE, UPDATE, or SPECIFY | ||
| * @return a SpecifyResult (specs are empty for CREATE/UPDATE, YAML for SPECIFY) | ||
| * @throws SQLException on validation or deployment errors | ||
| */ | ||
| static SpecifyResult processCreateJob(HoptimatorConnection conn, | ||
| SqlCreateJob create, DdlMode mode) throws SQLException { | ||
| HoptimatorConnection.HoptimatorConnectionDualLogger logger = conn.getLogger(HoptimatorDdlUtils.class); | ||
|
|
||
| logger.info("Validating statement: {}", create); | ||
| ValidationService.validateOrThrow(create); | ||
|
|
||
| if (create.name.names.size() > 1) { | ||
| throw new SQLException("Job names cannot be compound identifiers."); | ||
| } | ||
| String name = create.name.names.get(0); | ||
| String sqlBody = ((SqlLiteral) create.sqlBody).getValueAs(String.class); | ||
|
|
||
| // Split SQL body on semicolons into individual statements | ||
| List<String> sqlStatements = new ArrayList<>(); | ||
| for (String stmt : sqlBody.split(";")) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. guess this split(";") may create edge-case conditions for any ';' character inside a Flink connector option value. noticed this since we saw something similar here, https://github.com/linkedin/Hoptimator/pull/199/changes
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good call will fix |
||
| String trimmed = stmt.trim(); | ||
| if (!trimmed.isEmpty()) { | ||
| sqlStatements.add(trimmed); | ||
| } | ||
| } | ||
| if (sqlStatements.isEmpty()) { | ||
| throw new SQLException("Job " + name + " has no SQL statements."); | ||
| } | ||
|
|
||
| Map<String, String> jobOptions = options(create.options); | ||
| SqlJobDeployable job = new SqlJobDeployable(name, create.dialect, create.executionMode, | ||
| sqlStatements, jobOptions); | ||
|
|
||
| Collection<Deployer> deployers = null; | ||
| try { | ||
| logger.info("Validating job {}", name); | ||
| ValidationService.validateOrThrow(job); | ||
| deployers = DeploymentService.deployers(job, conn); | ||
| ValidationService.validateOrThrow(deployers); | ||
|
|
||
| List<String> specs = mode.executeDeployers(deployers, conn); | ||
| if (mode.mutable()) { | ||
| logger.info("Deployed job {}", name); | ||
| } else { | ||
| DeploymentService.restore(deployers); | ||
| } | ||
| return new SpecifyResult(specs, null, Collections.singletonList(name)); | ||
| } catch (SQLException | RuntimeException e) { | ||
| logger.info("Failed to deploy job {}", name); | ||
| if (deployers != null) { | ||
| DeploymentService.restore(deployers); | ||
| logger.info("Restored deployable resources for job {}", name); | ||
| } | ||
| throw e; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns the YAML specs that would be created for any supported SQL statement — | ||
| * {@code CREATE TABLE}, {@code CREATE MATERIALIZED VIEW}, or {@code INSERT INTO}. | ||
|
|
@@ -698,6 +763,10 @@ public static SpecifyResult specifyFromSql(String sql, HoptimatorConnection conn | |
| return processCreateDatabase(conn, (SqlCreateDatabase) sqlNode, DdlMode.SPECIFY); | ||
| } | ||
|
|
||
| if (sqlNode instanceof SqlCreateJob) { | ||
| return processCreateJob(conn, (SqlCreateJob) sqlNode, DdlMode.SPECIFY); | ||
| } | ||
|
|
||
| if (sqlNode instanceof SqlCreateTable) { | ||
| return processCreateTable(conn.createPrepareContext(), conn, (SqlCreateTable) sqlNode, DdlMode.SPECIFY); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More so naming wise but does feel awkward that we now have
SqlJobDeployableandJob(which deploys SqlJobs via templates) implementing the same interface but do semi-different things - not a deal breaker