mirror of https://github.com/tstack/lnav
[prql] initial work for integrating PRQL
parent
1113320cd4
commit
bdc9c5a28d
@ -0,0 +1,2 @@
|
||||
|
||||
let json_each = func input -> s"SELECT * FROM json_each({input})"
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "prqlc-c"
|
||||
publish = false
|
||||
version = "0.11.3"
|
||||
|
||||
edition = "2021"
|
||||
rust-version = "1.70.0"
|
||||
|
||||
# This means we can build with `--features=default`, which can make builds more generic
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[lib]
|
||||
# We produce both of these at the moment, but could consider refining this. ref
|
||||
# https://github.com/rust-lang/cargo/issues/8607 &
|
||||
# https://github.com/rust-lang/rust/issues/59302
|
||||
crate_type = ["staticlib", "cdylib"]
|
||||
doctest = false
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.153"
|
||||
prqlc = { git = "https://github.com/PRQL/prql.git" }
|
||||
serde_json = "1.0.114"
|
||||
|
||||
[package.metadata.release]
|
||||
tag-name = "{{version}}"
|
||||
tag-prefix = ""
|
@ -0,0 +1,102 @@
|
||||
# PRQL C library
|
||||
|
||||
## Description
|
||||
|
||||
This module compiles PRQL as a library (both `.a` and `.so` are generated). This
|
||||
allows embedding in languages that support FFI — for example, Golang.
|
||||
|
||||
## Linking
|
||||
|
||||
See [examples/minimal-c/Makefile](examples/minimal-c/Makefile).
|
||||
|
||||
Copy the `.a` and `.so` files in a convenient place and add the following
|
||||
compile flags to Go (cgo):
|
||||
|
||||
`CGO_LDFLAGS="-L/path/to/libprqlc_c.a -lprqlc -pthread -ldl" go build`
|
||||
|
||||
## Examples
|
||||
|
||||
For a minimal example, see
|
||||
[examples/minimal-c/main.c](examples/minimal-c/main.c).
|
||||
|
||||
Below is an example from an actual application that is using PRQL in Go.
|
||||
|
||||
```go
|
||||
package prql
|
||||
|
||||
/*
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int to_sql(char *prql_query, char *sql_query);
|
||||
int to_json(char *prql_query, char *json_query);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ToSQL converts a PRQL query to SQL
|
||||
func ToSQL(prql string) (string, error) {
|
||||
// buffer length should not be less than 1K because we may get an error
|
||||
// from the PRQL compiler with a very short query
|
||||
cStringBufferLength := 1024
|
||||
|
||||
// allocate a buffer 3 times the length of the PRQL query to store the
|
||||
// generated SQL query
|
||||
if len(prql)*3 > cStringBufferLength {
|
||||
cStringBufferLength = len(prql) * 3
|
||||
}
|
||||
|
||||
// preallocate the buffer
|
||||
cstr := C.CString(strings.Repeat(" ", cStringBufferLength))
|
||||
defer C.free(unsafe.Pointer(cstr))
|
||||
|
||||
// convert the PRQL query to SQL
|
||||
res := C.to_sql(C.CString(prql), cstr)
|
||||
if res == 0 {
|
||||
return C.GoString(cstr), nil
|
||||
}
|
||||
|
||||
return "", errors.New(C.GoString(cstr))
|
||||
}
|
||||
|
||||
// ToJSON converts a PRQL query to JSON
|
||||
func ToJSON(prql string) (string, error) {
|
||||
// buffer length should not be less than 1K because we may get an error
|
||||
cStringBufferLength := 1024
|
||||
if len(prql)*3 > cStringBufferLength {
|
||||
cStringBufferLength = len(prql) * 10
|
||||
}
|
||||
|
||||
// preallocate the buffer
|
||||
cstr := C.CString(strings.Repeat(" ", cStringBufferLength))
|
||||
defer C.free(unsafe.Pointer(cstr))
|
||||
|
||||
// convert the PRQL query to SQL
|
||||
res := C.to_json(C.CString(prql), cstr)
|
||||
if res == 0 {
|
||||
return C.GoString(cstr), nil
|
||||
}
|
||||
|
||||
return "", errors.New(C.GoString(cstr))
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Headers
|
||||
|
||||
The C & C++ header files `prqlc.h` & `prqlc.hpp` were generated using
|
||||
[cbindgen](https://github.com/eqrion/cbindgen). To generate a new one run:
|
||||
|
||||
```sh
|
||||
task build-prqlc-c-header
|
||||
```
|
||||
|
||||
...or copy & paste the commands from the Taskfile.
|
@ -0,0 +1,14 @@
|
||||
language = "C"
|
||||
|
||||
header = '''/*
|
||||
* PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement
|
||||
*
|
||||
* License: Apache-2.0
|
||||
* Website: https://prql-lang.org/
|
||||
*/'''
|
||||
|
||||
autogen_warning = "/* This file is autogenerated. Do not modify this file manually. */"
|
||||
|
||||
namespace = "prqlc"
|
||||
|
||||
after_includes = '#define FFI_SCOPE "PRQL"'
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* PRQL is a modern language for transforming data — a simple, powerful,
|
||||
* pipelined SQL replacement
|
||||
*
|
||||
* License: Apache-2.0
|
||||
* Website: https://prql-lang.org/
|
||||
*/
|
||||
|
||||
/* This file is autogenerated. Do not modify this file manually. */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#define FFI_SCOPE "PRQL"
|
||||
|
||||
/**
|
||||
* Compile message kind. Currently only Error is implemented.
|
||||
*/
|
||||
typedef enum MessageKind {
|
||||
Error,
|
||||
Warning,
|
||||
Lint,
|
||||
} MessageKind;
|
||||
|
||||
/**
|
||||
* Identifier of a location in source.
|
||||
* Contains offsets in terms of chars.
|
||||
*/
|
||||
typedef struct Span {
|
||||
size_t start;
|
||||
size_t end;
|
||||
} Span;
|
||||
|
||||
/**
|
||||
* Location within a source file.
|
||||
*/
|
||||
typedef struct SourceLocation {
|
||||
size_t start_line;
|
||||
size_t start_col;
|
||||
size_t end_line;
|
||||
size_t end_col;
|
||||
} SourceLocation;
|
||||
|
||||
/**
|
||||
* Compile result message.
|
||||
*
|
||||
* Calling code is responsible for freeing all memory allocated
|
||||
* for fields as well as strings.
|
||||
*/
|
||||
typedef struct Message {
|
||||
/**
|
||||
* Message kind. Currently only Error is implemented.
|
||||
*/
|
||||
enum MessageKind kind;
|
||||
/**
|
||||
* Machine-readable identifier of the error
|
||||
*/
|
||||
const char *const *code;
|
||||
/**
|
||||
* Plain text of the error
|
||||
*/
|
||||
const char *reason;
|
||||
/**
|
||||
* A list of suggestions of how to fix the error
|
||||
*/
|
||||
const char *const *hint;
|
||||
/**
|
||||
* Character offset of error origin within a source file
|
||||
*/
|
||||
const struct Span *span;
|
||||
/**
|
||||
* Annotated code, containing cause and hints.
|
||||
*/
|
||||
const char *const *display;
|
||||
/**
|
||||
* Line and column number of error origin within a source file
|
||||
*/
|
||||
const struct SourceLocation *location;
|
||||
} Message;
|
||||
|
||||
/**
|
||||
* Result of compilation.
|
||||
*/
|
||||
typedef struct CompileResult {
|
||||
const char *output;
|
||||
const struct Message *messages;
|
||||
size_t messages_len;
|
||||
} CompileResult;
|
||||
|
||||
/**
|
||||
* Compilation options
|
||||
*/
|
||||
typedef struct Options {
|
||||
/**
|
||||
* Pass generated SQL string trough a formatter that splits it
|
||||
* into multiple lines and prettifies indentation and spacing.
|
||||
*
|
||||
* Defaults to true.
|
||||
*/
|
||||
bool format;
|
||||
/**
|
||||
* Target and dialect to compile to.
|
||||
*
|
||||
* Defaults to `sql.any`, which uses `target` argument from the query header
|
||||
* to determine the SQL dialect.
|
||||
*/
|
||||
char *target;
|
||||
/**
|
||||
* Emits the compiler signature as a comment after generated SQL
|
||||
*
|
||||
* Defaults to true.
|
||||
*/
|
||||
bool signature_comment;
|
||||
} Options;
|
||||
|
||||
/**
|
||||
* Compile a PRQL string into a SQL string.
|
||||
*
|
||||
* This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without
|
||||
* converting to JSON between each of the functions.
|
||||
*
|
||||
* See `Options` struct for available compilation options.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* This function assumes zero-terminated input strings.
|
||||
* Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
* by calling `result_destroy`.
|
||||
*/
|
||||
struct CompileResult compile(const char *prql_query,
|
||||
const struct Options *options);
|
||||
|
||||
/**
|
||||
* Build PL AST from a PRQL string. PL in documented in the
|
||||
* [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/pl).
|
||||
*
|
||||
* Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer.
|
||||
*
|
||||
* Returns 0 on success and a negative number -1 on failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* This function assumes zero-terminated input strings.
|
||||
* Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
* by calling `result_destroy`.
|
||||
*/
|
||||
struct CompileResult prql_to_pl(const char *prql_query);
|
||||
|
||||
/**
|
||||
* Finds variable references, validates functions calls, determines frames and
|
||||
* converts PL to RQ. PL and RQ are documented in the [prqlc Rust
|
||||
* crate](https://docs.rs/prqlc/latest/prqlc/ast).
|
||||
*
|
||||
* Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out`
|
||||
* buffer.
|
||||
*
|
||||
* Returns 0 on success and a negative number -1 on failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* This function assumes zero-terminated input strings.
|
||||
* Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
* by calling `result_destroy`.
|
||||
*/
|
||||
struct CompileResult pl_to_rq(const char *pl_json);
|
||||
|
||||
/**
|
||||
* Convert RQ AST into an SQL string. RQ is documented in the
|
||||
* [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/rq).
|
||||
*
|
||||
* Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer.
|
||||
*
|
||||
* Returns 0 on success and a negative number -1 on failure.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* This function assumes zero-terminated input strings.
|
||||
* Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
* by calling `result_destroy`.
|
||||
*/
|
||||
struct CompileResult rq_to_sql(const char *rq_json,
|
||||
const struct Options *options);
|
||||
|
||||
/**
|
||||
* Destroy a `CompileResult` once you are done with it.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* This function expects to be called exactly once after the call of any the
|
||||
* functions that return `CompileResult`. No fields should be freed manually.
|
||||
*/
|
||||
void result_destroy(struct CompileResult res);
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* PRQL is a modern language for transforming data — a simple, powerful,
|
||||
* pipelined SQL replacement
|
||||
*
|
||||
* License: Apache-2.0
|
||||
* Website: https://prql-lang.org/
|
||||
*/
|
||||
|
||||
/* This file is autogenerated. Do not modify this file manually. */
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
#include <ostream>
|
||||
#define FFI_SCOPE "PRQL"
|
||||
|
||||
namespace prqlc {
|
||||
|
||||
/// Compile message kind. Currently only Error is implemented.
|
||||
enum class MessageKind {
|
||||
Error,
|
||||
Warning,
|
||||
Lint,
|
||||
};
|
||||
|
||||
/// Identifier of a location in source.
|
||||
/// Contains offsets in terms of chars.
|
||||
struct Span {
|
||||
size_t start;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
/// Location within a source file.
|
||||
struct SourceLocation {
|
||||
size_t start_line;
|
||||
size_t start_col;
|
||||
size_t end_line;
|
||||
size_t end_col;
|
||||
};
|
||||
|
||||
/// Compile result message.
|
||||
///
|
||||
/// Calling code is responsible for freeing all memory allocated
|
||||
/// for fields as well as strings.
|
||||
struct Message {
|
||||
/// Message kind. Currently only Error is implemented.
|
||||
MessageKind kind;
|
||||
/// Machine-readable identifier of the error
|
||||
const char* const* code;
|
||||
/// Plain text of the error
|
||||
const char* reason;
|
||||
/// A list of suggestions of how to fix the error
|
||||
const char* const* hint;
|
||||
/// Character offset of error origin within a source file
|
||||
const Span* span;
|
||||
/// Annotated code, containing cause and hints.
|
||||
const char* const* display;
|
||||
/// Line and column number of error origin within a source file
|
||||
const SourceLocation* location;
|
||||
};
|
||||
|
||||
/// Result of compilation.
|
||||
struct CompileResult {
|
||||
const char* output;
|
||||
const Message* messages;
|
||||
size_t messages_len;
|
||||
};
|
||||
|
||||
/// Compilation options
|
||||
struct Options {
|
||||
/// Pass generated SQL string trough a formatter that splits it
|
||||
/// into multiple lines and prettifies indentation and spacing.
|
||||
///
|
||||
/// Defaults to true.
|
||||
bool format;
|
||||
/// Target and dialect to compile to.
|
||||
///
|
||||
/// Defaults to `sql.any`, which uses `target` argument from the query
|
||||
/// header to determine the SQL dialect.
|
||||
const char* target;
|
||||
/// Emits the compiler signature as a comment after generated SQL
|
||||
///
|
||||
/// Defaults to true.
|
||||
bool signature_comment;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
/// Compile a PRQL string into a SQL string.
|
||||
///
|
||||
/// This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without
|
||||
/// converting to JSON between each of the functions.
|
||||
///
|
||||
/// See `Options` struct for available compilation options.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
CompileResult compile(const char* prql_query, const Options* options);
|
||||
|
||||
/// Build PL AST from a PRQL string. PL in documented in the
|
||||
/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/pl).
|
||||
///
|
||||
/// Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
CompileResult prql_to_pl(const char* prql_query);
|
||||
|
||||
/// Finds variable references, validates functions calls, determines frames and
|
||||
/// converts PL to RQ. PL and RQ are documented in the [prqlc Rust
|
||||
/// crate](https://docs.rs/prqlc/latest/prqlc/ast).
|
||||
///
|
||||
/// Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out`
|
||||
/// buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
CompileResult pl_to_rq(const char* pl_json);
|
||||
|
||||
/// Convert RQ AST into an SQL string. RQ is documented in the
|
||||
/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/rq).
|
||||
///
|
||||
/// Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
CompileResult rq_to_sql(const char* rq_json, const Options* options);
|
||||
|
||||
/// Destroy a `CompileResult` once you are done with it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function expects to be called exactly once after the call of any the
|
||||
/// functions that return `CompileResult`. No fields should be freed manually.
|
||||
void result_destroy(CompileResult res);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace prqlc
|
@ -0,0 +1,346 @@
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use libc::{c_char, size_t};
|
||||
use prqlc::{ErrorMessage, ErrorMessages};
|
||||
use prqlc::Target;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::panic;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Compile a PRQL string into a SQL string.
|
||||
///
|
||||
/// This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without converting to JSON
|
||||
/// between each of the functions.
|
||||
///
|
||||
/// See `Options` struct for available compilation options.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compile(
|
||||
prql_query: *const c_char,
|
||||
options: *const Options,
|
||||
) -> CompileResult {
|
||||
let prql_query: String = c_str_to_string(prql_query);
|
||||
|
||||
let options = options.as_ref().map(convert_options).transpose();
|
||||
|
||||
let result = options
|
||||
.and_then(|opts| {
|
||||
panic::catch_unwind(|| {
|
||||
Ok(prql_query.as_str())
|
||||
.and_then(prqlc::prql_to_pl)
|
||||
.and_then(prqlc::pl_to_rq)
|
||||
.and_then(|rq| prqlc::rq_to_sql(rq, &opts.unwrap_or_default()))
|
||||
}).map_err(|p| ErrorMessages {
|
||||
inner: vec![ErrorMessage {
|
||||
kind: prqlc::MessageKind::Error,
|
||||
code: None,
|
||||
reason: format!("internal error: {:#?}", p),
|
||||
hints: vec![],
|
||||
span: None,
|
||||
display: None,
|
||||
location: None,
|
||||
}]
|
||||
})?
|
||||
})
|
||||
.map_err(|e| e.composed(&prql_query.into()));
|
||||
|
||||
result_into_c_str(result)
|
||||
}
|
||||
|
||||
/// Build PL AST from a PRQL string. PL in documented in the
|
||||
/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/pl).
|
||||
///
|
||||
/// Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn prql_to_pl(prql_query: *const c_char) -> CompileResult {
|
||||
let prql_query: String = c_str_to_string(prql_query);
|
||||
|
||||
let result = Ok(prql_query.as_str())
|
||||
.and_then(prqlc::prql_to_pl)
|
||||
.and_then(|x| prqlc::json::from_pl(&x));
|
||||
result_into_c_str(result)
|
||||
}
|
||||
|
||||
/// Finds variable references, validates functions calls, determines frames and converts PL to RQ.
|
||||
/// PL and RQ are documented in the
|
||||
/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ast).
|
||||
///
|
||||
/// Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pl_to_rq(pl_json: *const c_char) -> CompileResult {
|
||||
let pl_json: String = c_str_to_string(pl_json);
|
||||
|
||||
let result = Ok(pl_json.as_str())
|
||||
.and_then(prqlc::json::to_pl)
|
||||
.and_then(prqlc::pl_to_rq)
|
||||
.and_then(|x| prqlc::json::from_rq(&x));
|
||||
result_into_c_str(result)
|
||||
}
|
||||
|
||||
/// Convert RQ AST into an SQL string. RQ is documented in the
|
||||
/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/rq).
|
||||
///
|
||||
/// Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer.
|
||||
///
|
||||
/// Returns 0 on success and a negative number -1 on failure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function assumes zero-terminated input strings.
|
||||
/// Calling code is responsible for freeing memory allocated for `CompileResult`
|
||||
/// by calling `result_destroy`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rq_to_sql(
|
||||
rq_json: *const c_char,
|
||||
options: *const Options,
|
||||
) -> CompileResult {
|
||||
let rq_json: String = c_str_to_string(rq_json);
|
||||
|
||||
let options = options.as_ref().map(convert_options).transpose();
|
||||
|
||||
let result = options.and_then(|options| {
|
||||
Ok(rq_json.as_str())
|
||||
.and_then(prqlc::json::to_rq)
|
||||
.and_then(|x| prqlc::rq_to_sql(x, &options.unwrap_or_default()))
|
||||
});
|
||||
result_into_c_str(result)
|
||||
}
|
||||
|
||||
/// Compilation options
|
||||
#[repr(C)]
|
||||
pub struct Options {
|
||||
/// Pass generated SQL string trough a formatter that splits it
|
||||
/// into multiple lines and prettifies indentation and spacing.
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub format: bool,
|
||||
|
||||
/// Target and dialect to compile to.
|
||||
///
|
||||
/// Defaults to `sql.any`, which uses `target` argument from the query header to determine
|
||||
/// the SQL dialect.
|
||||
pub target: *mut c_char,
|
||||
|
||||
/// Emits the compiler signature as a comment after generated SQL
|
||||
///
|
||||
/// Defaults to true.
|
||||
pub signature_comment: bool,
|
||||
}
|
||||
|
||||
/// Result of compilation.
|
||||
#[repr(C)]
|
||||
pub struct CompileResult {
|
||||
pub output: *const libc::c_char,
|
||||
pub messages: *const Message,
|
||||
pub messages_len: size_t,
|
||||
}
|
||||
|
||||
/// Compile message kind. Currently only Error is implemented.
|
||||
#[repr(C)]
|
||||
pub enum MessageKind {
|
||||
Error,
|
||||
Warning,
|
||||
Lint,
|
||||
}
|
||||
|
||||
/// Compile result message.
|
||||
///
|
||||
/// Calling code is responsible for freeing all memory allocated
|
||||
/// for fields as well as strings.
|
||||
// Make sure to keep in sync with prqlc::ErrorMessage
|
||||
#[repr(C)]
|
||||
pub struct Message {
|
||||
/// Message kind. Currently only Error is implemented.
|
||||
pub kind: MessageKind,
|
||||
/// Machine-readable identifier of the error
|
||||
pub code: *const *const libc::c_char,
|
||||
/// Plain text of the error
|
||||
pub reason: *const libc::c_char,
|
||||
/// A list of suggestions of how to fix the error
|
||||
pub hint: *const *const libc::c_char,
|
||||
/// Character offset of error origin within a source file
|
||||
pub span: *const Span,
|
||||
|
||||
/// Annotated code, containing cause and hints.
|
||||
pub display: *const *const libc::c_char,
|
||||
/// Line and column number of error origin within a source file
|
||||
pub location: *const SourceLocation,
|
||||
}
|
||||
|
||||
/// Identifier of a location in source.
|
||||
/// Contains offsets in terms of chars.
|
||||
// Make sure to keep in sync with prqlc::Span
|
||||
#[repr(C)]
|
||||
pub struct Span {
|
||||
pub start: size_t,
|
||||
pub end: size_t,
|
||||
}
|
||||
|
||||
/// Location within a source file.
|
||||
// Make sure to keep in sync with prqlc::SourceLocation
|
||||
#[repr(C)]
|
||||
pub struct SourceLocation {
|
||||
pub start_line: size_t,
|
||||
pub start_col: size_t,
|
||||
|
||||
pub end_line: size_t,
|
||||
pub end_col: size_t,
|
||||
}
|
||||
|
||||
/// Destroy a `CompileResult` once you are done with it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function expects to be called exactly once after the call of any the functions
|
||||
/// that return `CompileResult`. No fields should be freed manually.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn result_destroy(res: CompileResult) {
|
||||
// This is required because we are allocating memory for
|
||||
// strings, vectors and options.
|
||||
// For strings and vectors this is required, but options may be
|
||||
// able to live entirely within the struct, instead of the heap.
|
||||
|
||||
for i in 0..res.messages_len {
|
||||
let e = &*res.messages.add(i);
|
||||
|
||||
if !e.code.is_null() {
|
||||
drop(CString::from_raw(*e.code as *mut libc::c_char));
|
||||
drop(Box::from_raw(e.code as *mut *const libc::c_char));
|
||||
}
|
||||
drop(CString::from_raw(e.reason as *mut libc::c_char));
|
||||
if !e.hint.is_null() {
|
||||
drop(CString::from_raw(*e.hint as *mut libc::c_char));
|
||||
drop(Box::from_raw(e.hint as *mut *const libc::c_char));
|
||||
}
|
||||
if !e.span.is_null() {
|
||||
drop(Box::from_raw(e.span as *mut Span));
|
||||
}
|
||||
if !e.display.is_null() {
|
||||
drop(CString::from_raw(*e.display as *mut libc::c_char));
|
||||
drop(Box::from_raw(e.display as *mut *const libc::c_char));
|
||||
}
|
||||
if !e.location.is_null() {
|
||||
drop(Box::from_raw(e.location as *mut SourceLocation));
|
||||
}
|
||||
}
|
||||
drop(Vec::from_raw_parts(
|
||||
res.messages as *mut i8,
|
||||
res.messages_len,
|
||||
res.messages_len,
|
||||
));
|
||||
drop(CString::from_raw(res.output as *mut libc::c_char));
|
||||
}
|
||||
|
||||
unsafe fn result_into_c_str(result: Result<String, ErrorMessages>) -> CompileResult {
|
||||
match result {
|
||||
Ok(output) => CompileResult {
|
||||
output: convert_string(output),
|
||||
messages: ::std::ptr::null_mut(),
|
||||
messages_len: 0,
|
||||
},
|
||||
Err(err) => {
|
||||
let mut errors = Vec::with_capacity(err.inner.len());
|
||||
errors.extend(err.inner.into_iter().map(|e| Message {
|
||||
kind: MessageKind::Error,
|
||||
code: option_to_ptr(e.code.map(convert_string)),
|
||||
reason: convert_string(e.reason),
|
||||
hint: option_to_ptr(if e.hints.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(convert_string(e.hints.join("\n")))
|
||||
}),
|
||||
span: option_to_ptr(e.span.map(convert_span)),
|
||||
display: option_to_ptr(e.display.map(convert_string)),
|
||||
location: option_to_ptr(e.location.map(convert_source_location)),
|
||||
}));
|
||||
CompileResult {
|
||||
output: CString::default().into_raw(),
|
||||
messages_len: errors.len(),
|
||||
messages: errors.leak().as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates the value on the heap and returns a pointer to it.
|
||||
/// If the input is None, it returns null pointer.
|
||||
fn option_to_ptr<T>(o: Option<T>) -> *const T {
|
||||
match o {
|
||||
Some(x) => {
|
||||
let b = Box::new(x);
|
||||
Box::into_raw(b)
|
||||
}
|
||||
None => ::std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_string(x: String) -> *const libc::c_char {
|
||||
CString::new(x).unwrap_or_default().into_raw()
|
||||
}
|
||||
|
||||
fn convert_span(x: prqlc::Span) -> Span {
|
||||
Span {
|
||||
start: x.start,
|
||||
end: x.end,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_source_location(x: prqlc::SourceLocation) -> SourceLocation {
|
||||
SourceLocation {
|
||||
start_line: x.start.0,
|
||||
start_col: x.start.1,
|
||||
end_line: x.end.0,
|
||||
end_col: x.end.1,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn c_str_to_string(c_str: *const c_char) -> String {
|
||||
// inefficient, but simple
|
||||
CStr::from_ptr(c_str).to_string_lossy().into_owned()
|
||||
}
|
||||
|
||||
fn convert_options(o: &Options) -> Result<prqlc::Options, prqlc::ErrorMessages> {
|
||||
let target = if !o.target.is_null() {
|
||||
Some(unsafe { c_str_to_string(o.target) })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let target = target
|
||||
.as_deref()
|
||||
.filter(|x| !x.is_empty())
|
||||
.unwrap_or("sql.any");
|
||||
|
||||
let target = Target::from_str(target).map_err(prqlc::ErrorMessages::from)?;
|
||||
|
||||
Ok(prqlc::Options {
|
||||
format: o.format,
|
||||
target,
|
||||
signature_comment: o.signature_comment,
|
||||
// TODO: add support for this
|
||||
color: false,
|
||||
})
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#! /bin/bash
|
||||
|
||||
export TZ=UTC
|
||||
export YES_COLOR=1
|
||||
unset XDG_CONFIG_HOME
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ";from db.access_log | take 1" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ";from db.access_log | take abc" \
|
||||
${test_dir}/logfile_access_log.0
|
Loading…
Reference in New Issue