SmartSQL
SmartSQL transforms database interactions by combining traditional SQL capabilities with AI-powered natural language processing. Execute direct SQL queries when you need precision, or use plain English when exploring data or building user-facing interfaces. Every database operation includes automatic PII detection and metadata tracking to maintain compliance and optimize future AI-generated queries.
The system processes both structured SQL commands and conversational queries like “show me users who joined last month” through the same interface. Background services handle PII scanning and schema analysis without blocking query responses, giving you immediate results while maintaining data security and improving query intelligence over time.
SmartSQL includes external API support with Connect/gRPC endpoints, making it accessible from any client that needs secure database access with AI capabilities.
Prerequisites
- Basic understanding of SQL query syntax and database operations
- Raindrop framework installed and configured in your project
- Familiarity with TypeScript interfaces and async/await patterns
- Environment variables configured for AI services (Hugging Face, SambaNova)
Creating SmartSQL
Add SmartSQL to your application manifest to enable intelligent database operations:
application "demo-app" { smartsql "main-db" {}}
Run raindrop build generate
to create the necessary files and service bindings.
Accessing SmartSQL
SmartSQL becomes available through environment variables following Raindrop’s standard naming pattern. The name from your manifest converts to uppercase with underscores replacing dashes.
For example, smartsql "main-db"
becomes env.MAIN_DB
. This single interface handles both direct SQL execution and natural language processing.
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { // Execute direct SQL query const sqlResult = await this.env.MAIN_DB.executeQuery({ sqlQuery: 'SELECT * FROM users WHERE active = true', format: 'json' });
// Execute natural language query const nlResult = await this.env.MAIN_DB.executeQuery({ textQuery: 'Show me all active users from the last month', format: 'csv' });
return new Response(JSON.stringify({ sqlResult, nlResult })); }}
Core Concepts
Query Processing
SmartSQL handles two distinct input types through a unified interface. Direct SQL queries execute immediately against your database, while natural language queries convert to SQL using AI models before execution. The system validates generated SQL with safety tokens to prevent injection attacks while maintaining query flexibility.
Query results return immediately while background services handle PII detection and metadata updates. This keeps your applications responsive while building compliance documentation and improving AI query accuracy.
PII Detection Service
Automatic scanning analyzes all data modifications (INSERT, UPDATE operations) for personal information using Hugging Face models. Detection results are stored in a dedicated _pii
system table with indexed lookups for efficient retrieval.
The service identifies multiple entity types including names, emails, phone numbers, and addresses. Each detection includes confidence scores and precise text positions for detailed compliance reporting and data governance workflows.
Metadata Management
Schema intelligence tracks your database structure in a _meta
system table, storing column types, sample data, and relationships. This information improves AI query generation by providing context about your actual data model.
Metadata updates happen asynchronously through message queues. As your schema evolves, SmartSQL automatically adapts its understanding so AI-generated queries remain accurate and relevant to your current database structure.
Output Formats
Results support JSON format for structured data processing and CSV format for data export and analysis workflows. Both formats include the same result data with different serialization for integration with various downstream systems.
Query Operations
executeQuery()
Executes SQL queries or converts natural language to SQL with complete result information.
Parameters:
sqlQuery?: string
- Direct SQL query to executetextQuery?: string
- Natural language query to convertformat?: 'json' | 'csv'
- Output format (default: ‘json’)
Returns:
message: string
- Operation status messageresults?: string
- Query results in specified formatstatus: number
- HTTP status codequeryExecuted: string
- SQL query that was executedaiReasoning?: string
- AI reasoning for natural language queries
const result = await this.env.MAIN_DB.executeQuery({ sqlQuery: 'SELECT name, email FROM users WHERE created_at > ?', format: 'json'});
console.log(`Query: ${result.queryExecuted}`);console.log(`Status: ${result.status}`);const users = JSON.parse(result.results || '[]');
const result = await this.env.MAIN_DB.executeQuery({ textQuery: 'Find users who registered this week', format: 'csv'});
console.log(`AI Reasoning: ${result.aiReasoning}`);console.log(`Generated SQL: ${result.queryExecuted}`);// result.results contains CSV data
getMetadata()
Retrieves database schema information for AI query context and application logic.
Parameters:
tableName?: string
- Specific table to retrieve metadata for
Returns:
tables: TableMetadata[]
- Array of table schema informationlastUpdated?: string
- When metadata was last updated
interface TableMetadata { tableName: string; columns: Array<{ columnName: string; dataType: string; sampleData?: string; nullable: boolean; isPrimaryKey: boolean; }>; createdAt?: string; updatedAt?: string;}
const metadata = await this.env.MAIN_DB.getMetadata();
metadata.tables.forEach(table => { console.log(`Table: ${table.tableName}`); table.columns.forEach(column => { console.log(` ${column.columnName}: ${column.dataType}`); if (column.sampleData) { console.log(` Sample: ${column.sampleData}`); } });});
const userMetadata = await this.env.MAIN_DB.getMetadata('users');
if (userMetadata.tables.length > 0) { const table = userMetadata.tables[0]; console.log(`${table.tableName} has ${table.columns.length} columns`);}
updateMetadata()
Updates database schema metadata to improve AI query generation accuracy.
Parameters:
tables: TableMetadata[]
- Array of table metadata to updatemode?: 'replace' | 'merge' | 'append'
- Update strategy (default: ‘replace’)
Returns:
success: boolean
- Whether the update succeededtablesUpdated: number
- Number of tables updatedmessage: string
- Status message
const updateResult = await this.env.MAIN_DB.updateMetadata([ { tableName: 'products', columns: [ { columnName: 'id', dataType: 'INTEGER', sampleData: '1001', nullable: false, isPrimaryKey: true }, { columnName: 'name', dataType: 'TEXT', sampleData: 'Wireless Headphones', nullable: false, isPrimaryKey: false } ] }], 'merge');
if (updateResult.success) { console.log(`Updated ${updateResult.tablesUpdated} tables`);}
// Completely overwrites existing metadataawait this.env.MAIN_DB.updateMetadata(tables, 'replace');
// Updates provided fields, preserves existing dataawait this.env.MAIN_DB.updateMetadata(tables, 'merge');
// Only adds new entries, never updates existingawait this.env.MAIN_DB.updateMetadata(tables, 'append');
getPiiData()
Retrieves PII detection results for compliance reporting and data governance.
Parameters:
tableName: string
- Table name to retrieve PII data fromrecordId?: string
- Specific record identifier (row signature)
Returns:
piiDetections: PiiDetection[]
- Array of PII detection results
interface PiiDetection { detectionId: string; tableName: string; recordId: string; entities: Array<{ entityType: string; // 'EMAIL', 'PERSON', 'PHONE', etc. confidenceScore: number; // 0-1 confidence level detectedText: string; // The actual PII text found startPosition: number; // Start position in text endPosition: number; // End position in text tokenIndex: number; // Token position }>; detectedAt: string; // When PII was detected}
const allPii = await this.env.MAIN_DB.getPiiData('users');
allPii.piiDetections.forEach(detection => { console.log(`Record ${detection.recordId}:`); detection.entities.forEach(entity => { console.log(` ${entity.entityType}: ${entity.detectedText} (${entity.confidenceScore})`); });});
// First get the record data to generate its signatureconst record = await this.env.MAIN_DB.executeQuery({ sqlQuery: 'SELECT * FROM users WHERE id = ?', // Bind the actual record ID value here});
// Then get PII data using the record identifierconst recordPii = await this.env.MAIN_DB.getPiiData('users', 'user_123');
External API Access
SmartSQL provides Connect/gRPC endpoints for external clients with JWT authentication and request validation.
API Configuration
Protocol: Connect/gRPC over HTTP
Content-Type: application/connect+json
Authentication: Bearer token (JWT) with user and organization claims
Base URL: Your deployed SmartSQL service endpoint
Authentication Requirements
All API requests require valid JWT tokens with matching identity claims.
- JWT Token - Valid token in Authorization header
- User Validation -
userId
in request must match JWT claims - Organization Validation -
organizationId
in request must match JWT claims - Module Access - Permission to access the specified SmartSQL module
SmartSQL Location
API requests identify target modules using smartSqlLocation
:
{ "smartSqlLocation": { "moduleId": "your-module-id" }}
API Endpoints
ExecuteQuery
Endpoint: smartsql.SmartSqlService/ExecuteQuery
Execute SQL or natural language queries through the external API.
{ "smartSqlLocation": { "moduleId": "prod-database-module" }, "sqlQuery": "SELECT * FROM orders WHERE status = 'pending'", "format": "JSON", "userId": "user_01ABC123DEF456", "organizationId": "org_01XYZ789GHI012"}
{ "message": "Query executed successfully", "results": { "jsonResults": "[{\"id\": 1, \"status\": \"pending\", \"total\": 129.99}]" }, "status": 200, "queryExecuted": "SELECT * FROM orders WHERE status = 'pending'", "aiReasoning": ""}
GetMetadata
Endpoint: smartsql.SmartSqlService/GetMetadata
Retrieve database schema information through the API.
{ "smartSqlLocation": { "moduleId": "prod-database-module" }, "tableName": "products", "userId": "user_01ABC123DEF456", "organizationId": "org_01XYZ789GHI012"}
{ "tables": [ { "tableName": "products", "columns": [ { "columnName": "id", "dataType": "INTEGER", "sampleData": "1001", "nullable": false, "isPrimaryKey": true } ] } ]}
UpdateMetadata
Endpoint: smartsql.SmartSqlService/UpdateMetadata
Update database schema metadata through the API.
{ "smartSqlLocation": { "moduleId": "prod-database-module" }, "tables": [ { "tableName": "inventory", "columns": [ { "columnName": "sku", "dataType": "TEXT", "sampleData": "WH-1000XM4", "nullable": false, "isPrimaryKey": true } ] } ], "userId": "user_01ABC123DEF456", "organizationId": "org_01XYZ789GHI012"}
{ "success": true, "tablesUpdated": 1, "message": "Metadata updated successfully"}
GetPiiData
Endpoint: smartsql.SmartSqlService/GetPiiData
Retrieve PII detection results through the API.
{ "smartSqlLocation": { "moduleId": "prod-database-module" }, "tableName": "customer_data", "recordId": "cust_456", "userId": "user_01ABC123DEF456", "organizationId": "org_01XYZ789GHI012"}
{ "piiDetections": [ { "detectionId": "detection_789", "tableName": "customer_data", "recordId": "cust_456", "entities": [ { "entityType": "EMAIL", "confidenceScore": 0.98, "detectedText": "customer@example.com", "startPosition": 0, "endPosition": 20 } ] } ]}
JavaScript Client Example
class SmartSqlClient { constructor(baseUrl, token, userId, organizationId) { this.baseUrl = baseUrl; this.token = token; this.userId = userId; this.organizationId = organizationId; }
async executeQuery(moduleId, options) { const response = await fetch(`${this.baseUrl}smartsql.SmartSqlService/ExecuteQuery`, { method: 'POST', headers: { 'Content-Type': 'application/connect+json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ smartSqlLocation: { moduleId }, sqlQuery: options.sqlQuery, textQuery: options.textQuery, format: options.format || 'JSON', userId: this.userId, organizationId: this.organizationId }) });
if (!response.ok) { throw new Error(`API request failed: ${response.status}`); }
return await response.json(); }
async getMetadata(moduleId, tableName) { const response = await fetch(`${this.baseUrl}smartsql.SmartSqlService/GetMetadata`, { method: 'POST', headers: { 'Content-Type': 'application/connect+json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ smartSqlLocation: { moduleId }, tableName, userId: this.userId, organizationId: this.organizationId }) });
if (!response.ok) { throw new Error(`Metadata request failed: ${response.status}`); }
return await response.json(); }}
// Usageconst client = new SmartSqlClient( 'https://your-smartsql-endpoint.com/', 'your-jwt-token', 'user_01ABC123DEF456', 'org_01XYZ789GHI012');
// Execute a natural language queryconst result = await client.executeQuery('my-module-id', { textQuery: 'Show me top selling products this month', format: 'JSON'});
console.log(`AI generated: ${result.queryExecuted}`);console.log(`Results: ${result.results.jsonResults}`);
Error Handling
API responses use standard Connect/gRPC error codes:
INVALID_ARGUMENT
: Missing or invalid request parametersPERMISSION_DENIED
: Authentication failed or insufficient permissionsINTERNAL
: Server-side processing errorsUNAUTHENTICATED
: Invalid or missing JWT token
All requests validate using Zod schemas with detailed error messages for validation failures.
System Tables
SmartSQL creates and manages two system tables for PII detection and metadata storage:
_pii Table
Stores PII detection results with indexed lookups for efficient compliance reporting:
CREATE TABLE _pii ( id TEXT PRIMARY KEY, table_name TEXT NOT NULL, row_signature TEXT NOT NULL, pii_data TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);CREATE INDEX idx_pii_table_signature ON _pii(table_name, row_signature);
_meta Table
Maintains database schema information for AI query generation:
CREATE TABLE _meta ( id TEXT PRIMARY KEY, table_name TEXT NOT NULL, column_name TEXT NOT NULL, data_type TEXT NOT NULL, sample_data TEXT, UNIQUE(table_name, column_name));
Configuration
Required environment variables for AI services:
HF_API_TOKEN
: Hugging Face API token for PII detection modelsHF_ENDPOINT
: Hugging Face model endpoint URLSAMBA_KEY
: SambaNova API key for natural language processingRAINBOW_MGMT_API_TOKEN
: Rainbow management API token
Code Examples
Complete Application Example
import { Service } from '@liquidmetal-ai/raindrop-framework';import { Env } from './raindrop.gen';
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); const path = url.pathname;
try { switch (path) { case '/search': return this.handleSearch(request); case '/metadata': return this.handleMetadata(); case '/pii-report': return this.handlePiiReport(); default: return new Response('Not found', { status: 404 }); } } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }
private async handleSearch(request: Request): Promise<Response> { const { query, format = 'json' } = await request.json();
// Try natural language first, fallback to direct SQL const result = await this.env.MAIN_DB.executeQuery({ textQuery: query, format: format as 'json' | 'csv' });
if (result.status !== 200) { // Fallback to treating as direct SQL const sqlResult = await this.env.MAIN_DB.executeQuery({ sqlQuery: query, format: format as 'json' | 'csv' });
return new Response(JSON.stringify(sqlResult), { headers: { 'Content-Type': 'application/json' } }); }
return new Response(JSON.stringify({ ...result, wasNaturalLanguage: true }), { headers: { 'Content-Type': 'application/json' } }); }
private async handleMetadata(): Promise<Response> { const metadata = await this.env.MAIN_DB.getMetadata();
// Enrich with table statistics const enrichedTables = await Promise.all( metadata.tables.map(async (table) => { const countResult = await this.env.MAIN_DB.executeQuery({ sqlQuery: `SELECT COUNT(*) as row_count FROM ${table.tableName}`, format: 'json' });
const rowCount = countResult.results ? JSON.parse(countResult.results)[0]?.row_count || 0 : 0;
return { ...table, rowCount }; }) );
return new Response(JSON.stringify({ tables: enrichedTables, totalTables: enrichedTables.length, lastUpdated: metadata.lastUpdated }), { headers: { 'Content-Type': 'application/json' } }); }
private async handlePiiReport(): Promise<Response> { // Get all tables from metadata const metadata = await this.env.MAIN_DB.getMetadata();
// Check each table for PII const piiReport = await Promise.all( metadata.tables.map(async (table) => { const piiData = await this.env.MAIN_DB.getPiiData(table.tableName);
return { tableName: table.tableName, piiDetections: piiData.piiDetections.length, entities: piiData.piiDetections.flatMap(d => d.entities.map(e => e.entityType) ).filter((v, i, a) => a.indexOf(v) === i) // unique }; }) );
const totalDetections = piiReport.reduce((sum, table) => sum + table.piiDetections, 0);
return new Response(JSON.stringify({ summary: { totalTables: piiReport.length, totalDetections, tablesWithPii: piiReport.filter(t => t.piiDetections > 0).length }, details: piiReport }), { headers: { 'Content-Type': 'application/json' } }); }}
Metadata Health Check System
class MetadataHealthChecker { constructor(private smartSql: any) {}
async performHealthCheck(tableName?: string): Promise<void> { const metadata = await this.smartSql.getMetadata(tableName);
for (const table of metadata.tables) { const columnsWithoutSamples = table.columns.filter(col => !col.sampleData);
if (columnsWithoutSamples.length > 0) { console.log(`Updating samples for ${table.tableName}`); await this.updateSampleData(table.tableName, columnsWithoutSamples); } } }
private async updateSampleData(tableName: string, columns: any[]): Promise<void> { // Get sample data for columns const sampleQuery = `SELECT ${columns.map(c => c.columnName).join(', ')} FROM ${tableName} LIMIT 5`; const sampleResult = await this.smartSql.executeQuery({ sqlQuery: sampleQuery, format: 'json' });
if (sampleResult.status === 200 && sampleResult.results) { const samples = JSON.parse(sampleResult.results); if (samples.length > 0) { const firstRow = samples[0];
// Update metadata with sample data const updatedColumns = columns.map(column => ({ ...column, sampleData: String(firstRow[column.columnName] || '') }));
await this.smartSql.updateMetadata([{ tableName, columns: updatedColumns }], 'merge'); } } }}
// Usage in your serviceconst healthChecker = new MetadataHealthChecker(this.env.MAIN_DB);await healthChecker.performHealthCheck(); // Check all tablesawait healthChecker.performHealthCheck('users'); // Check specific table