Skip to content

Commit 9e70009

Browse files
committed
feat: add SQL schema parsing and constraint extraction
Add new schema module with support for: - Parsing CREATE TABLE, CREATE INDEX, and ALTER TABLE statements - Extracting primary keys, foreign keys, and indexes - Supporting multiple SQL dialects (PostgreSQL, MySQL, SQLite, Generic) - Helper functions for constraint analysis and validation Includes comprehensive test coverage and integration with existing plugin framework. Signed-off-by: Svetlin Ralchev <iamralch@users.noreply.github.com>
1 parent 9d11ac6 commit 9e70009

File tree

5 files changed

+785
-12
lines changed

5 files changed

+785
-12
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ description = "A crate for building sqlc plugins"
77

88
[dependencies]
99
prost = "0.14.1"
10+
sqlparser = "0.50"
1011

1112
[build-dependencies]
1213
prost-build = "0.14.1"

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
//! Provides:
44
//! - `plugin`: generated proto definitions
55
//! - `runtime`: helper functions for running sqlc.dev plugins
6+
//! - `schema`: SQL schema parsing and constraint extraction
67
78
pub mod plugin;
89
pub mod runtime;
10+
pub mod schema;
911

1012
pub mod prelude {
11-
pub use crate::plugin::*;
12-
pub use crate::runtime::*;
13+
pub use crate::plugin::{File, GenerateRequest, GenerateResponse};
1314
pub use prost::Message;
1415
}

src/runtime.rs

Lines changed: 141 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,157 @@
1+
//! Runtime infrastructure for sqlc plugin execution.
2+
//!
3+
//! This module provides the core runtime functions for sqlc plugins, handling
4+
//! the communication protocol between sqlc and the plugin. It reads protobuf-encoded
5+
//! requests from stdin, processes them through a user-provided handler, and writes
6+
//! protobuf-encoded responses to stdout.
7+
//!
8+
//! # Overview
9+
//!
10+
//! The runtime handles:
11+
//! - Reading and decoding protobuf messages from stdin
12+
//! - Invoking user-defined code generation logic
13+
//! - Encoding and writing responses back to stdout
14+
//! - Error propagation and handling
15+
//!
16+
//! # Example
17+
//!
18+
//! ```no_run
19+
//! use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File};
20+
//! use sqlc_gen_core::runtime::run;
21+
//!
22+
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
23+
//! run(|request| {
24+
//! // Your code generation logic here
25+
//! let files = vec![File {
26+
//! name: "output.rs".to_string(),
27+
//! contents: b"// Generated code".to_vec(),
28+
//! }];
29+
//!
30+
//! Ok(GenerateResponse { files })
31+
//! })
32+
//! }
33+
//! ```
34+
135
use crate::plugin::{GenerateRequest, GenerateResponse};
236
use prost::Message;
337
use std::error::Error;
438
use std::io::{Read, Write};
539

6-
pub fn run<TFunc>(process: TFunc) -> Result<(), Box<dyn Error>>
40+
/// Runs a sqlc plugin with the standard stdin/stdout communication protocol.
41+
///
42+
/// This is the main entry point for sqlc plugins. It reads a protobuf-encoded
43+
/// [`GenerateRequest`] from stdin, passes it to your processing function, and
44+
/// writes the resulting [`GenerateResponse`] back to stdout.
45+
///
46+
/// # Arguments
47+
///
48+
/// * `process` - A function that takes a [`GenerateRequest`] and returns a
49+
/// [`GenerateResponse`]. This is where your code generation logic should live.
50+
///
51+
/// # Errors
52+
///
53+
/// Returns an error if:
54+
/// - Reading from stdin fails
55+
/// - Decoding the protobuf request fails
56+
/// - The process function returns an error
57+
/// - Encoding the response fails
58+
/// - Writing to stdout fails
59+
///
60+
/// # Example
61+
///
62+
/// ```no_run
63+
/// use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File};
64+
/// use sqlc_gen_core::runtime::run;
65+
///
66+
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
67+
/// run(|request| {
68+
/// // Access request data
69+
/// let version = &request.sqlc_version;
70+
/// let queries = &request.queries;
71+
///
72+
/// // Generate files based on the request
73+
/// let files = vec![File {
74+
/// name: "generated.rs".to_string(),
75+
/// contents: b"// Generated code".to_vec(),
76+
/// }];
77+
///
78+
/// Ok(GenerateResponse { files })
79+
/// })
80+
/// }
81+
/// ```
82+
pub fn run<F>(process: F) -> Result<(), Box<dyn Error>>
783
where
8-
TFunc: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>,
84+
F: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>,
985
{
1086
let stdin = std::io::stdin();
1187
let stdout = std::io::stdout();
1288
run_with_io(stdin.lock(), stdout.lock(), process)
1389
}
1490

15-
pub fn run_with_io<TReader, TWriter, TFunc>(
16-
mut reader: TReader,
17-
mut writer: TWriter,
18-
process: TFunc,
19-
) -> Result<(), Box<dyn Error>>
91+
/// Runs a sqlc plugin with custom I/O streams.
92+
///
93+
/// This is a more flexible version of [`run`] that allows you to provide custom
94+
/// readers and writers instead of using stdin/stdout. This is particularly useful
95+
/// for testing, as it allows you to pass in-memory buffers instead of actual I/O streams.
96+
///
97+
/// # Arguments
98+
///
99+
/// * `reader` - An input stream containing the protobuf-encoded [`GenerateRequest`]
100+
/// * `writer` - An output stream where the protobuf-encoded [`GenerateResponse`] will be written
101+
/// * `process` - A function that takes a [`GenerateRequest`] and returns a [`GenerateResponse`]
102+
///
103+
/// # Type Parameters
104+
///
105+
/// * `R` - Any type that implements [`Read`]
106+
/// * `W` - Any type that implements [`Write`]
107+
/// * `F` - A closure that processes the request and returns a response
108+
///
109+
/// # Errors
110+
///
111+
/// Returns an error if:
112+
/// - Reading from the input stream fails
113+
/// - Decoding the protobuf request fails
114+
/// - The process function returns an error
115+
/// - Encoding the response fails
116+
/// - Writing to the output stream fails
117+
///
118+
/// # Example
119+
///
120+
/// ```
121+
/// use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File};
122+
/// use sqlc_gen_core::runtime::run_with_io;
123+
/// use prost::Message;
124+
///
125+
/// // Create a test request
126+
/// let request = GenerateRequest {
127+
/// sqlc_version: "1.0.0".to_string(),
128+
/// settings: None,
129+
/// catalog: None,
130+
/// queries: vec![],
131+
/// plugin_options: vec![],
132+
/// global_options: vec![],
133+
/// };
134+
///
135+
/// // Encode it
136+
/// let mut input = Vec::new();
137+
/// request.encode(&mut input).unwrap();
138+
///
139+
/// // Process it
140+
/// let mut output = Vec::new();
141+
/// run_with_io(&input[..], &mut output, |req| {
142+
/// assert_eq!(req.sqlc_version, "1.0.0");
143+
/// Ok(GenerateResponse { files: vec![] })
144+
/// }).unwrap();
145+
///
146+
/// // Decode the response
147+
/// let response = GenerateResponse::decode(&output[..]).unwrap();
148+
/// assert_eq!(response.files.len(), 0);
149+
/// ```
150+
pub fn run_with_io<R, W, F>(mut reader: R, mut writer: W, process: F) -> Result<(), Box<dyn Error>>
20151
where
21-
TReader: Read,
22-
TWriter: Write,
23-
TFunc: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>,
152+
R: Read,
153+
W: Write,
154+
F: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>,
24155
{
25156
let mut input = Vec::new();
26157
reader.read_to_end(&mut input)?;

0 commit comments

Comments
 (0)