2
0

main.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "flag"
  6. "fmt"
  7. "net/http"
  8. "os"
  9. "os/signal"
  10. "strings"
  11. "syscall"
  12. "time"
  13. "github.com/danfragoso/pizzasql-next/pkg/executor"
  14. "github.com/danfragoso/pizzasql-next/pkg/httpserver"
  15. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  16. "github.com/danfragoso/pizzasql-next/pkg/parser"
  17. "github.com/danfragoso/pizzasql-next/pkg/storage"
  18. )
  19. var (
  20. kvAddr = flag.String("kv", "localhost:8085", "PizzaKV server address")
  21. database = flag.String("db", "pizzasql", "Database name")
  22. poolSize = flag.Int("pool", 5, "Connection pool size")
  23. timeout = flag.Duration("timeout", 30*time.Second, "Query timeout")
  24. httpEnable = flag.Bool("http", false, "Enable HTTP server")
  25. httpHost = flag.String("http-host", "localhost", "HTTP server host")
  26. httpPort = flag.Int("http-port", 8080, "HTTP server port")
  27. httpCORS = flag.Bool("http-cors", true, "Enable CORS")
  28. httpAuth = flag.Bool("http-auth", false, "Enable authentication")
  29. apiKeys = flag.String("api-keys", "", "Comma-separated API keys")
  30. )
  31. func main() {
  32. flag.Parse()
  33. // Check if HTTP server mode is enabled
  34. if *httpEnable {
  35. runHTTPServer()
  36. return
  37. }
  38. // Check for command-line SQL
  39. args := flag.Args()
  40. if len(args) > 0 {
  41. // Execute single SQL statement
  42. sql := strings.Join(args, " ")
  43. executeSingle(sql)
  44. return
  45. }
  46. // Check for piped input
  47. stat, _ := os.Stdin.Stat()
  48. if (stat.Mode() & os.ModeCharDevice) == 0 {
  49. // Input is from pipe
  50. executePipe()
  51. return
  52. }
  53. // Interactive REPL mode
  54. runREPL()
  55. }
  56. func executeSingle(sql string) {
  57. // Try to connect to PizzaKV
  58. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  59. if err != nil {
  60. // Fall back to expression-only mode
  61. executeExpressionOnly(sql)
  62. return
  63. }
  64. defer pool.Close()
  65. schema := storage.NewSchemaManager(pool, *database)
  66. table := storage.NewTableManager(pool, schema, *database)
  67. exec := executor.New(schema, table)
  68. exec.SyncCatalog()
  69. result, err := executeSQL(exec, sql)
  70. if err != nil {
  71. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  72. os.Exit(1)
  73. }
  74. fmt.Print(result.String())
  75. }
  76. func executePipe() {
  77. // Try to connect to PizzaKV
  78. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  79. if err != nil {
  80. // Fall back to expression-only mode
  81. scanner := bufio.NewScanner(os.Stdin)
  82. for scanner.Scan() {
  83. sql := strings.TrimSpace(scanner.Text())
  84. if sql == "" || strings.HasPrefix(sql, "--") {
  85. continue
  86. }
  87. executeExpressionOnly(sql)
  88. }
  89. return
  90. }
  91. defer pool.Close()
  92. schema := storage.NewSchemaManager(pool, *database)
  93. table := storage.NewTableManager(pool, schema, *database)
  94. exec := executor.New(schema, table)
  95. exec.SyncCatalog()
  96. scanner := bufio.NewScanner(os.Stdin)
  97. for scanner.Scan() {
  98. sql := strings.TrimSpace(scanner.Text())
  99. if sql == "" || strings.HasPrefix(sql, "--") {
  100. continue
  101. }
  102. result, err := executeSQL(exec, sql)
  103. if err != nil {
  104. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  105. continue
  106. }
  107. fmt.Print(result.String())
  108. }
  109. }
  110. func runREPL() {
  111. fmt.Println("PizzaSQL - SQL-92 compatible database")
  112. fmt.Println("Type 'help' for usage, 'quit' to exit")
  113. fmt.Println()
  114. // Try to connect to PizzaKV
  115. var pool *storage.KVPool
  116. var schema *storage.SchemaManager
  117. var table *storage.TableManager
  118. var exec *executor.Executor
  119. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  120. if err != nil {
  121. fmt.Printf("Warning: Cannot connect to PizzaKV at %s\n", *kvAddr)
  122. fmt.Println("Running in expression-only mode (no table storage)")
  123. fmt.Println()
  124. } else {
  125. schema = storage.NewSchemaManager(pool, *database)
  126. table = storage.NewTableManager(pool, schema, *database)
  127. exec = executor.New(schema, table)
  128. exec.SyncCatalog()
  129. fmt.Printf("Connected to PizzaKV at %s (database: %s)\n\n", *kvAddr, *database)
  130. }
  131. reader := bufio.NewReader(os.Stdin)
  132. var sqlBuffer strings.Builder
  133. for {
  134. if sqlBuffer.Len() == 0 {
  135. fmt.Print("pizzasql> ")
  136. } else {
  137. fmt.Print(" -> ")
  138. }
  139. line, err := reader.ReadString('\n')
  140. if err != nil {
  141. fmt.Println()
  142. break
  143. }
  144. line = strings.TrimSpace(line)
  145. // Handle special commands
  146. switch strings.ToLower(line) {
  147. case "quit", "exit", "\\q":
  148. fmt.Println("Goodbye!")
  149. if pool != nil {
  150. pool.Close()
  151. }
  152. return
  153. case "help", "\\h":
  154. printHelp()
  155. continue
  156. case "tables", "\\dt":
  157. if schema != nil {
  158. listTables(schema)
  159. } else {
  160. fmt.Println("Not connected to database")
  161. }
  162. continue
  163. case "clear", "\\c":
  164. sqlBuffer.Reset()
  165. fmt.Println("Buffer cleared")
  166. continue
  167. }
  168. // Skip empty lines and comments
  169. if line == "" || strings.HasPrefix(line, "--") {
  170. continue
  171. }
  172. // Accumulate SQL
  173. if sqlBuffer.Len() > 0 {
  174. sqlBuffer.WriteString(" ")
  175. }
  176. sqlBuffer.WriteString(line)
  177. // Check if statement is complete (ends with semicolon)
  178. sql := sqlBuffer.String()
  179. if !strings.HasSuffix(sql, ";") {
  180. continue
  181. }
  182. // Remove semicolon and execute
  183. sql = strings.TrimSuffix(sql, ";")
  184. sqlBuffer.Reset()
  185. if exec != nil {
  186. result, err := executeSQL(exec, sql)
  187. if err != nil {
  188. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  189. continue
  190. }
  191. fmt.Print(result.String())
  192. } else {
  193. executeExpressionOnly(sql)
  194. }
  195. }
  196. }
  197. func executeSQL(exec *executor.Executor, sql string) (*executor.Result, error) {
  198. l := lexer.New(sql)
  199. p := parser.New(l)
  200. stmt, err := p.Parse()
  201. if err != nil {
  202. return nil, fmt.Errorf("parse error: %w", err)
  203. }
  204. return exec.Execute(stmt)
  205. }
  206. func executeExpressionOnly(sql string) {
  207. l := lexer.New(sql)
  208. p := parser.New(l)
  209. stmt, err := p.Parse()
  210. if err != nil {
  211. fmt.Fprintf(os.Stderr, "Parse error: %v\n", err)
  212. return
  213. }
  214. // For SELECT statements without FROM, we can evaluate expressions
  215. if sel, ok := stmt.(*parser.SelectStmt); ok && len(sel.From) == 0 {
  216. exec := &executor.Executor{}
  217. result, err := executeSelectExpr(exec, sel)
  218. if err != nil {
  219. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  220. return
  221. }
  222. fmt.Print(result.String())
  223. return
  224. }
  225. // For other statements, just print what was parsed
  226. switch s := stmt.(type) {
  227. case *parser.SelectStmt:
  228. fmt.Printf("SELECT statement with %d columns\n", len(s.Columns))
  229. if len(s.From) > 0 {
  230. fmt.Printf(" FROM: %s\n", s.From[0].Name)
  231. }
  232. if s.Where != nil {
  233. fmt.Println(" WHERE: <condition>")
  234. }
  235. fmt.Println("(Not connected to database - cannot execute)")
  236. case *parser.InsertStmt:
  237. fmt.Printf("INSERT into %s (%d rows)\n", s.Table.Name, len(s.Values))
  238. fmt.Println("(Not connected to database - cannot execute)")
  239. case *parser.UpdateStmt:
  240. fmt.Printf("UPDATE %s (%d assignments)\n", s.Table.Name, len(s.Set))
  241. fmt.Println("(Not connected to database - cannot execute)")
  242. case *parser.DeleteStmt:
  243. fmt.Printf("DELETE from %s\n", s.Table.Name)
  244. fmt.Println("(Not connected to database - cannot execute)")
  245. case *parser.CreateTableStmt:
  246. fmt.Printf("CREATE TABLE %s (%d columns)\n", s.Table.Name, len(s.Columns))
  247. fmt.Println("(Not connected to database - cannot execute)")
  248. case *parser.DropTableStmt:
  249. fmt.Printf("DROP TABLE %s\n", s.Tables[0].Name)
  250. fmt.Println("(Not connected to database - cannot execute)")
  251. default:
  252. fmt.Printf("Parsed: %T\n", stmt)
  253. }
  254. }
  255. // executeSelectExpr handles SELECT without FROM (expression evaluation)
  256. func executeSelectExpr(exec *executor.Executor, stmt *parser.SelectStmt) (*executor.Result, error) {
  257. result := executor.NewResult("SELECT")
  258. // Determine columns
  259. for i, col := range stmt.Columns {
  260. if col.Alias != "" {
  261. result.AddColumn(col.Alias)
  262. } else {
  263. result.AddColumn(fmt.Sprintf("column%d", i+1))
  264. }
  265. }
  266. // Evaluate expressions using reflection to access private method
  267. // For simplicity, we'll use a minimal evaluator here
  268. values := make([]interface{}, len(stmt.Columns))
  269. for i, col := range stmt.Columns {
  270. val, err := evalExprSimple(col.Expr)
  271. if err != nil {
  272. return nil, err
  273. }
  274. values[i] = val
  275. }
  276. result.AddRow(values...)
  277. return result, nil
  278. }
  279. // evalExprSimple is a simplified expression evaluator for standalone expressions
  280. func evalExprSimple(expr parser.Expr) (interface{}, error) {
  281. switch e := expr.(type) {
  282. case *parser.LiteralExpr:
  283. switch e.Type {
  284. case lexer.TokenNumber:
  285. if strings.Contains(e.Value, ".") {
  286. var f float64
  287. fmt.Sscanf(e.Value, "%f", &f)
  288. return f, nil
  289. }
  290. var i int64
  291. fmt.Sscanf(e.Value, "%d", &i)
  292. return i, nil
  293. case lexer.TokenString:
  294. return e.Value, nil
  295. case lexer.TokenNULL:
  296. return nil, nil
  297. case lexer.TokenTRUE:
  298. return true, nil
  299. case lexer.TokenFALSE:
  300. return false, nil
  301. }
  302. case *parser.BinaryExpr:
  303. left, err := evalExprSimple(e.Left)
  304. if err != nil {
  305. return nil, err
  306. }
  307. right, err := evalExprSimple(e.Right)
  308. if err != nil {
  309. return nil, err
  310. }
  311. return evalBinarySimple(e.Op, left, right)
  312. case *parser.UnaryExpr:
  313. val, err := evalExprSimple(e.Operand)
  314. if err != nil {
  315. return nil, err
  316. }
  317. switch e.Op {
  318. case lexer.TokenMinus:
  319. return -toFloatSimple(val), nil
  320. case lexer.TokenNOT:
  321. return !toBoolSimple(val), nil
  322. }
  323. return val, nil
  324. case *parser.ParenExpr:
  325. return evalExprSimple(e.Expr)
  326. }
  327. return nil, fmt.Errorf("unsupported expression type: %T", expr)
  328. }
  329. func evalBinarySimple(op lexer.TokenType, left, right interface{}) (interface{}, error) {
  330. switch op {
  331. case lexer.TokenPlus:
  332. return toFloatSimple(left) + toFloatSimple(right), nil
  333. case lexer.TokenMinus:
  334. return toFloatSimple(left) - toFloatSimple(right), nil
  335. case lexer.TokenStar:
  336. return toFloatSimple(left) * toFloatSimple(right), nil
  337. case lexer.TokenSlash:
  338. r := toFloatSimple(right)
  339. if r == 0 {
  340. return nil, nil
  341. }
  342. return toFloatSimple(left) / r, nil
  343. case lexer.TokenEq:
  344. return compareSimple(left, right) == 0, nil
  345. case lexer.TokenNeq:
  346. return compareSimple(left, right) != 0, nil
  347. case lexer.TokenLt:
  348. return compareSimple(left, right) < 0, nil
  349. case lexer.TokenGt:
  350. return compareSimple(left, right) > 0, nil
  351. case lexer.TokenLte:
  352. return compareSimple(left, right) <= 0, nil
  353. case lexer.TokenGte:
  354. return compareSimple(left, right) >= 0, nil
  355. case lexer.TokenAND:
  356. return toBoolSimple(left) && toBoolSimple(right), nil
  357. case lexer.TokenOR:
  358. return toBoolSimple(left) || toBoolSimple(right), nil
  359. }
  360. return nil, fmt.Errorf("unsupported operator: %v", op)
  361. }
  362. func toFloatSimple(v interface{}) float64 {
  363. switch val := v.(type) {
  364. case int64:
  365. return float64(val)
  366. case float64:
  367. return val
  368. case bool:
  369. if val {
  370. return 1
  371. }
  372. return 0
  373. }
  374. return 0
  375. }
  376. func toBoolSimple(v interface{}) bool {
  377. switch val := v.(type) {
  378. case bool:
  379. return val
  380. case int64:
  381. return val != 0
  382. case float64:
  383. return val != 0
  384. }
  385. return false
  386. }
  387. func compareSimple(a, b interface{}) int {
  388. fa := toFloatSimple(a)
  389. fb := toFloatSimple(b)
  390. if fa < fb {
  391. return -1
  392. }
  393. if fa > fb {
  394. return 1
  395. }
  396. return 0
  397. }
  398. func printHelp() {
  399. fmt.Println("PizzaSQL Commands:")
  400. fmt.Println(" help, \\h Show this help")
  401. fmt.Println(" quit, \\q Exit the program")
  402. fmt.Println(" tables, \\dt List all tables")
  403. fmt.Println(" clear, \\c Clear the input buffer")
  404. fmt.Println()
  405. fmt.Println("SQL Statements (end with semicolon):")
  406. fmt.Println(" SELECT ... FROM ... WHERE ...")
  407. fmt.Println(" INSERT INTO table (cols) VALUES (...)")
  408. fmt.Println(" UPDATE table SET col = val WHERE ...")
  409. fmt.Println(" DELETE FROM table WHERE ...")
  410. fmt.Println(" CREATE TABLE table (col TYPE, ...)")
  411. fmt.Println(" DROP TABLE table")
  412. fmt.Println()
  413. fmt.Println("Expression Mode (SELECT without FROM):")
  414. fmt.Println(" SELECT 1 + 2 * 3;")
  415. fmt.Println(" SELECT UPPER('hello');")
  416. }
  417. func listTables(schema *storage.SchemaManager) {
  418. tables, err := schema.ListTables()
  419. if err != nil {
  420. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  421. return
  422. }
  423. if len(tables) == 0 {
  424. fmt.Println("No tables found")
  425. return
  426. }
  427. fmt.Println("Tables:")
  428. for _, t := range tables {
  429. fmt.Printf(" %s\n", t)
  430. }
  431. }
  432. func runHTTPServer() {
  433. // Connect to PizzaKV
  434. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  435. if err != nil {
  436. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  437. fmt.Fprintf(os.Stderr, "Make sure PizzaKV is running: pizzakv\n")
  438. os.Exit(1)
  439. }
  440. defer pool.Close()
  441. // Create schema and executor
  442. schema := storage.NewSchemaManager(pool, *database)
  443. table := storage.NewTableManager(pool, schema, *database)
  444. exec := executor.New(schema, table)
  445. // Configure HTTP server
  446. config := httpserver.DefaultConfig()
  447. config.Host = *httpHost
  448. config.Port = *httpPort
  449. config.EnableCORS = *httpCORS
  450. config.EnableAuth = *httpAuth
  451. if *apiKeys != "" {
  452. config.APIKeys = strings.Split(*apiKeys, ",")
  453. }
  454. // Create and start server
  455. server := httpserver.New(config, exec, schema)
  456. // Handle graceful shutdown
  457. stop := make(chan os.Signal, 1)
  458. signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
  459. // Start server in goroutine
  460. go func() {
  461. if err := server.Start(); err != nil && err != http.ErrServerClosed {
  462. fmt.Fprintf(os.Stderr, "HTTP server error: %v\n", err)
  463. os.Exit(1)
  464. }
  465. }()
  466. fmt.Printf("PizzaSQL HTTP server started on http://%s:%d\n", *httpHost, *httpPort)
  467. fmt.Printf("Database: %s\n", *database)
  468. fmt.Printf("PizzaKV: %s\n", *kvAddr)
  469. fmt.Println()
  470. fmt.Println("Endpoints:")
  471. fmt.Println(" POST /query - Execute SQL query")
  472. fmt.Println(" POST /execute - Batch execution")
  473. fmt.Println(" GET /schema/tables - List tables")
  474. fmt.Println(" GET /schema/tables/{name} - Table schema")
  475. fmt.Println(" GET /health - Health check")
  476. fmt.Println(" GET /stats - Statistics")
  477. fmt.Println(" GET /metrics - Prometheus metrics")
  478. fmt.Println(" POST /transaction/begin - Begin transaction")
  479. fmt.Println(" POST /transaction/commit - Commit transaction")
  480. fmt.Println(" POST /transaction/rollback - Rollback transaction")
  481. fmt.Println()
  482. fmt.Println("Example:")
  483. fmt.Printf(" curl -X POST http://%s:%d/query -H 'Content-Type: application/json' -d '{\"sql\":\"SELECT 1+1\"}'\n", *httpHost, *httpPort)
  484. fmt.Println()
  485. fmt.Println("Press Ctrl+C to stop")
  486. <-stop
  487. fmt.Println("\nShutting down server...")
  488. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  489. defer cancel()
  490. if err := server.Shutdown(ctx); err != nil {
  491. fmt.Fprintf(os.Stderr, "Error during shutdown: %v\n", err)
  492. }
  493. fmt.Println("Server stopped")
  494. }