|
37 | 37 | //! `name`, `type`, `status`, `stage`, …), not the wire rows. |
38 | 38 |
|
39 | 39 | use crate::client::ingest::{ |
40 | | - ConnectorEntry, IngestAck, IngestClient, IngestRequest, JobStatus, QueryToIngest, |
| 40 | + ConnectorEntry, IngestAck, IngestClient, IngestRequest, JobStatus, QueryToIngest, RawSqlIngest, |
41 | 41 | }; |
42 | 42 | use crate::commands::prompt::{ |
43 | 43 | ask_secret, ask_text, optional, optional_default, prompt_list, select_optional, |
@@ -160,6 +160,34 @@ pub enum IngestCommands { |
160 | 160 | poll: PollArgs, |
161 | 161 | }, |
162 | 162 |
|
| 163 | + /// Ingest the result of a source-native SQL query (SQL datasources only) |
| 164 | + /// |
| 165 | + /// Unlike `new-import` (SELECT <cols|*> FROM <ds>[.<table>] [WHERE] [LIMIT]), |
| 166 | + /// the SQL runs verbatim against the source engine in its OWN dialect — |
| 167 | + /// joins, aggregation, GROUP BY, CTEs, window functions, and engine-specific |
| 168 | + /// functions all execute at the source; only the result set transfers. It |
| 169 | + /// must be a single read-only statement; the result lands in `--table` in a |
| 170 | + /// fresh managed database. |
| 171 | + RawSql { |
| 172 | + /// A single read-only SQL statement, in the SOURCE engine's dialect |
| 173 | + sql: String, |
| 174 | + |
| 175 | + /// SQL datasource to run against: a name or a datasource id |
| 176 | + #[arg(long)] |
| 177 | + source: String, |
| 178 | + |
| 179 | + /// Result table name in the new managed database |
| 180 | + #[arg(long)] |
| 181 | + table: String, |
| 182 | + |
| 183 | + /// Cap the result at N rows |
| 184 | + #[arg(long)] |
| 185 | + limit: Option<u64>, |
| 186 | + |
| 187 | + #[command(flatten)] |
| 188 | + poll: PollArgs, |
| 189 | + }, |
| 190 | + |
163 | 191 | /// List your imports: the SQL behind each and the database it landed in |
164 | 192 | ListImports, |
165 | 193 |
|
@@ -252,6 +280,13 @@ pub fn dispatch(workspace_id: &str, output: &str, command: IngestCommands) { |
252 | 280 | database_id, |
253 | 281 | poll, |
254 | 282 | } => new_import(workspace_id, output, sql, all, source, database_id, &poll), |
| 283 | + IngestCommands::RawSql { |
| 284 | + sql, |
| 285 | + source, |
| 286 | + table, |
| 287 | + limit, |
| 288 | + poll, |
| 289 | + } => raw_sql(workspace_id, output, sql, source, table, limit, &poll), |
255 | 290 | IngestCommands::ListImports => list_imports(workspace_id, output), |
256 | 291 | IngestCommands::TriggerImport { id, poll } => { |
257 | 292 | trigger_import(workspace_id, output, &id, &poll) |
@@ -940,6 +975,48 @@ fn looks_like_ingest_id(s: &str) -> bool { |
940 | 975 | s.len() == 32 && s.chars().all(|c| c.is_ascii_hexdigit()) |
941 | 976 | } |
942 | 977 |
|
| 978 | +// --- raw-sql: source-native SQL front-door -------------------------------- |
| 979 | + |
| 980 | +fn raw_sql( |
| 981 | + workspace_id: &str, |
| 982 | + output: &str, |
| 983 | + sql: String, |
| 984 | + source: String, |
| 985 | + table: String, |
| 986 | + limit: Option<u64>, |
| 987 | + poll: &PollArgs, |
| 988 | +) { |
| 989 | + let client = IngestClient::new(workspace_id); |
| 990 | + let req = RawSqlIngest { |
| 991 | + sql, |
| 992 | + source, |
| 993 | + table, |
| 994 | + limit, |
| 995 | + }; |
| 996 | + let spinner = util::spinner("submitting raw-sql import…"); |
| 997 | + let ack = client.create_raw_query(&req).unwrap_or_else(|e| { |
| 998 | + spinner.finish_and_clear(); |
| 999 | + e.exit() |
| 1000 | + }); |
| 1001 | + spinner.finish_and_clear(); |
| 1002 | + if poll.wait { |
| 1003 | + let st = poll_ingest( |
| 1004 | + &client, |
| 1005 | + output, |
| 1006 | + &ack.ingest_id, |
| 1007 | + poll.wait_timeout, |
| 1008 | + "importing", |
| 1009 | + true, |
| 1010 | + ); |
| 1011 | + render_done(&st, output); |
| 1012 | + return; |
| 1013 | + } |
| 1014 | + // Non-blocking default: kick the drain so the enqueued job actually runs, |
| 1015 | + // then hand the terminal back (same as new-import). |
| 1016 | + let _ = client.drain(); |
| 1017 | + render_ack(&ack, "import id:", output); |
| 1018 | +} |
| 1019 | + |
943 | 1020 | /// The SQL a `new-import` runs: explicit SQL wins; `--all` becomes a full |
944 | 1021 | /// `SELECT *` against the datasource's name (`pinned_name` when --source was |
945 | 1022 | /// a datasource id). Errors are messages for `fail`. |
|
0 commit comments