main.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  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/csvexport"
  14. "github.com/danfragoso/pizzasql-next/pkg/csvimport"
  15. "github.com/danfragoso/pizzasql-next/pkg/executor"
  16. "github.com/danfragoso/pizzasql-next/pkg/httpserver"
  17. "github.com/danfragoso/pizzasql-next/pkg/kvmanager"
  18. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  19. "github.com/danfragoso/pizzasql-next/pkg/parser"
  20. "github.com/danfragoso/pizzasql-next/pkg/pgserver"
  21. "github.com/danfragoso/pizzasql-next/pkg/sqlexport"
  22. "github.com/danfragoso/pizzasql-next/pkg/sqlimport"
  23. "github.com/danfragoso/pizzasql-next/pkg/storage"
  24. )
  25. var (
  26. kvAddr = flag.String("kvaddr", "localhost:8085", "PizzaKV server address (ignored if -kv is set)")
  27. kvLaunch = flag.Bool("kv", false, "Launch PizzaKV automatically")
  28. kvFlags = flag.String("kvflags", "", "Flags to pass to PizzaKV (e.g., \"-iwal -port=9090\")")
  29. kvInfoFile = flag.String("kvinfo", ".pizzakv.json", "Path to PizzaKV info file")
  30. database = flag.String("db", "pizzasql", "Database name")
  31. poolSize = flag.Int("pool", 5, "Connection pool size")
  32. timeout = flag.Duration("timeout", 30*time.Second, "Query timeout")
  33. httpEnable = flag.Bool("http", false, "Enable HTTP server")
  34. httpHost = flag.String("http-host", "localhost", "HTTP server host")
  35. httpPort = flag.Int("http-port", 8080, "HTTP server port")
  36. httpCORS = flag.Bool("http-cors", true, "Enable CORS")
  37. httpAuth = flag.Bool("http-auth", false, "Enable authentication")
  38. httpCompression = flag.Bool("http-compression", true, "Enable HTTP response compression")
  39. quiet = flag.Bool("quiet", false, "Disable request/query logging")
  40. apiKeys = flag.String("api-keys", "", "Comma-separated API keys")
  41. // PostgreSQL wire protocol server flags
  42. pgEnable = flag.Bool("pg", false, "Enable PostgreSQL wire protocol server")
  43. pgHost = flag.String("pg-host", "localhost", "PostgreSQL server host")
  44. pgPort = flag.Int("pg-port", 5432, "PostgreSQL server port")
  45. // Export/Import flags
  46. exportFile = flag.String("o", "", "Output file for export")
  47. importFile = flag.String("i", "", "Input file for import")
  48. exportTable = flag.String("table", "", "Specific table to export (empty = all)")
  49. exportDrop = flag.Bool("drop", false, "Include DROP TABLE statements in export")
  50. ignoreErrors = flag.Bool("ignore-errors", false, "Continue import on errors")
  51. exportFormat = flag.String("format", "", "Export/import format: sql, csv (auto-detect from extension)")
  52. createTable = flag.Bool("create-table", false, "Create table if not exists (CSV import)")
  53. )
  54. var kvManager *kvmanager.Manager
  55. var startPprofServerHook func() *http.Server
  56. func main() {
  57. flag.Parse()
  58. // If -kv flag is set, launch PizzaKV
  59. if *kvLaunch {
  60. if err := launchPizzaKV(); err != nil {
  61. fmt.Fprintf(os.Stderr, "Failed to launch PizzaKV: %v\n", err)
  62. os.Exit(1)
  63. }
  64. defer stopPizzaKV()
  65. // Set up signal handling for graceful shutdown
  66. sigChan := make(chan os.Signal, 1)
  67. signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
  68. go func() {
  69. <-sigChan
  70. fmt.Println("\nShutting down...")
  71. stopPizzaKV()
  72. os.Exit(0)
  73. }()
  74. }
  75. // Check if HTTP server mode is enabled
  76. if *httpEnable {
  77. runHTTPServer()
  78. return
  79. }
  80. // Check if PostgreSQL server mode is enabled
  81. if *pgEnable {
  82. runPGServer()
  83. return
  84. }
  85. // Check for export command
  86. if *exportFile != "" {
  87. runExport()
  88. return
  89. }
  90. // Check for import command
  91. if *importFile != "" {
  92. runImport()
  93. return
  94. }
  95. // Check for command-line SQL
  96. args := flag.Args()
  97. if len(args) > 0 {
  98. // Execute single SQL statement
  99. sql := strings.Join(args, " ")
  100. executeSingle(sql)
  101. return
  102. }
  103. // Check for piped input
  104. stat, _ := os.Stdin.Stat()
  105. if (stat.Mode() & os.ModeCharDevice) == 0 {
  106. // Input is from pipe
  107. executePipe()
  108. return
  109. }
  110. // Interactive REPL mode
  111. runREPL()
  112. }
  113. func executeSingle(sql string) {
  114. // Try to connect to PizzaKV
  115. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  116. if err != nil {
  117. // Fall back to expression-only mode
  118. executeExpressionOnly(sql)
  119. return
  120. }
  121. defer pool.Close()
  122. schema := storage.NewSchemaManager(pool, *database)
  123. table := storage.NewTableManager(pool, schema, *database)
  124. exec := executor.New(schema, table)
  125. exec.SyncCatalog()
  126. result, err := executeSQL(exec, sql)
  127. if err != nil {
  128. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  129. os.Exit(1)
  130. }
  131. fmt.Print(result.String())
  132. }
  133. func executePipe() {
  134. // Try to connect to PizzaKV
  135. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  136. if err != nil {
  137. // Fall back to expression-only mode
  138. scanner := bufio.NewScanner(os.Stdin)
  139. for scanner.Scan() {
  140. sql := strings.TrimSpace(scanner.Text())
  141. if sql == "" || strings.HasPrefix(sql, "--") {
  142. continue
  143. }
  144. executeExpressionOnly(sql)
  145. }
  146. return
  147. }
  148. defer pool.Close()
  149. schema := storage.NewSchemaManager(pool, *database)
  150. table := storage.NewTableManager(pool, schema, *database)
  151. exec := executor.New(schema, table)
  152. exec.SyncCatalog()
  153. scanner := bufio.NewScanner(os.Stdin)
  154. for scanner.Scan() {
  155. sql := strings.TrimSpace(scanner.Text())
  156. if sql == "" || strings.HasPrefix(sql, "--") {
  157. continue
  158. }
  159. result, err := executeSQL(exec, sql)
  160. if err != nil {
  161. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  162. continue
  163. }
  164. fmt.Print(result.String())
  165. }
  166. }
  167. func runREPL() {
  168. fmt.Println("PizzaSQL - SQL-92 compatible database")
  169. fmt.Println("Type 'help' for usage, 'quit' to exit")
  170. fmt.Println()
  171. // Try to connect to PizzaKV
  172. var pool *storage.KVPool
  173. var schema *storage.SchemaManager
  174. var table *storage.TableManager
  175. var exec *executor.Executor
  176. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  177. if err != nil {
  178. fmt.Printf("Warning: Cannot connect to PizzaKV at %s\n", *kvAddr)
  179. fmt.Println("Running in expression-only mode (no table storage)")
  180. fmt.Println()
  181. } else {
  182. schema = storage.NewSchemaManager(pool, *database)
  183. table = storage.NewTableManager(pool, schema, *database)
  184. exec = executor.New(schema, table)
  185. exec.SyncCatalog()
  186. fmt.Printf("Connected to PizzaKV at %s (database: %s)\n\n", *kvAddr, *database)
  187. }
  188. reader := bufio.NewReader(os.Stdin)
  189. var sqlBuffer strings.Builder
  190. for {
  191. if sqlBuffer.Len() == 0 {
  192. fmt.Print("pizzasql> ")
  193. } else {
  194. fmt.Print(" -> ")
  195. }
  196. line, err := reader.ReadString('\n')
  197. if err != nil {
  198. fmt.Println()
  199. break
  200. }
  201. line = strings.TrimSpace(line)
  202. // Handle special commands
  203. switch strings.ToLower(line) {
  204. case "quit", "exit", "\\q":
  205. fmt.Println("Goodbye!")
  206. if pool != nil {
  207. pool.Close()
  208. }
  209. return
  210. case "help", "\\h":
  211. printHelp()
  212. continue
  213. case "tables", "\\dt":
  214. if schema != nil {
  215. listTables(schema)
  216. } else {
  217. fmt.Println("Not connected to database")
  218. }
  219. continue
  220. case "clear", "\\c":
  221. sqlBuffer.Reset()
  222. fmt.Println("Buffer cleared")
  223. continue
  224. }
  225. // Skip empty lines and comments
  226. if line == "" || strings.HasPrefix(line, "--") {
  227. continue
  228. }
  229. // Accumulate SQL
  230. if sqlBuffer.Len() > 0 {
  231. sqlBuffer.WriteString(" ")
  232. }
  233. sqlBuffer.WriteString(line)
  234. // Check if statement is complete (ends with semicolon)
  235. sql := sqlBuffer.String()
  236. if !strings.HasSuffix(sql, ";") {
  237. continue
  238. }
  239. // Remove semicolon and execute
  240. sql = strings.TrimSuffix(sql, ";")
  241. sqlBuffer.Reset()
  242. if exec != nil {
  243. result, err := executeSQL(exec, sql)
  244. if err != nil {
  245. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  246. continue
  247. }
  248. fmt.Print(result.String())
  249. } else {
  250. executeExpressionOnly(sql)
  251. }
  252. }
  253. }
  254. func executeSQL(exec *executor.Executor, sql string) (*executor.Result, error) {
  255. l := lexer.New(sql)
  256. p := parser.New(l)
  257. stmt, err := p.Parse()
  258. if err != nil {
  259. return nil, fmt.Errorf("parse error: %w", err)
  260. }
  261. return exec.Execute(stmt)
  262. }
  263. func executeExpressionOnly(sql string) {
  264. l := lexer.New(sql)
  265. p := parser.New(l)
  266. stmt, err := p.Parse()
  267. if err != nil {
  268. fmt.Fprintf(os.Stderr, "Parse error: %v\n", err)
  269. return
  270. }
  271. // For SELECT statements without FROM, we can evaluate expressions
  272. if sel, ok := stmt.(*parser.SelectStmt); ok && len(sel.From) == 0 {
  273. exec := &executor.Executor{}
  274. result, err := executeSelectExpr(exec, sel)
  275. if err != nil {
  276. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  277. return
  278. }
  279. fmt.Print(result.String())
  280. return
  281. }
  282. // For other statements, just print what was parsed
  283. switch s := stmt.(type) {
  284. case *parser.SelectStmt:
  285. fmt.Printf("SELECT statement with %d columns\n", len(s.Columns))
  286. if len(s.From) > 0 {
  287. fmt.Printf(" FROM: %s\n", s.From[0].Name)
  288. }
  289. if s.Where != nil {
  290. fmt.Println(" WHERE: <condition>")
  291. }
  292. fmt.Println("(Not connected to database - cannot execute)")
  293. case *parser.InsertStmt:
  294. fmt.Printf("INSERT into %s (%d rows)\n", s.Table.Name, len(s.Values))
  295. fmt.Println("(Not connected to database - cannot execute)")
  296. case *parser.UpdateStmt:
  297. fmt.Printf("UPDATE %s (%d assignments)\n", s.Table.Name, len(s.Set))
  298. fmt.Println("(Not connected to database - cannot execute)")
  299. case *parser.DeleteStmt:
  300. fmt.Printf("DELETE from %s\n", s.Table.Name)
  301. fmt.Println("(Not connected to database - cannot execute)")
  302. case *parser.CreateTableStmt:
  303. fmt.Printf("CREATE TABLE %s (%d columns)\n", s.Table.Name, len(s.Columns))
  304. fmt.Println("(Not connected to database - cannot execute)")
  305. case *parser.DropTableStmt:
  306. fmt.Printf("DROP TABLE %s\n", s.Tables[0].Name)
  307. fmt.Println("(Not connected to database - cannot execute)")
  308. default:
  309. fmt.Printf("Parsed: %T\n", stmt)
  310. }
  311. }
  312. // executeSelectExpr handles SELECT without FROM (expression evaluation)
  313. func executeSelectExpr(exec *executor.Executor, stmt *parser.SelectStmt) (*executor.Result, error) {
  314. result := executor.NewResult("SELECT")
  315. // Determine columns
  316. for i, col := range stmt.Columns {
  317. if col.Alias != "" {
  318. result.AddColumn(col.Alias)
  319. } else {
  320. result.AddColumn(fmt.Sprintf("column%d", i+1))
  321. }
  322. }
  323. // Evaluate expressions using reflection to access private method
  324. // For simplicity, we'll use a minimal evaluator here
  325. values := make([]interface{}, len(stmt.Columns))
  326. for i, col := range stmt.Columns {
  327. val, err := evalExprSimple(col.Expr)
  328. if err != nil {
  329. return nil, err
  330. }
  331. values[i] = val
  332. }
  333. result.AddRow(values...)
  334. return result, nil
  335. }
  336. // evalExprSimple is a simplified expression evaluator for standalone expressions
  337. func evalExprSimple(expr parser.Expr) (interface{}, error) {
  338. switch e := expr.(type) {
  339. case *parser.LiteralExpr:
  340. switch e.Type {
  341. case lexer.TokenNumber:
  342. if strings.Contains(e.Value, ".") {
  343. var f float64
  344. fmt.Sscanf(e.Value, "%f", &f)
  345. return f, nil
  346. }
  347. var i int64
  348. fmt.Sscanf(e.Value, "%d", &i)
  349. return i, nil
  350. case lexer.TokenString:
  351. return e.Value, nil
  352. case lexer.TokenNULL:
  353. return nil, nil
  354. case lexer.TokenTRUE:
  355. return true, nil
  356. case lexer.TokenFALSE:
  357. return false, nil
  358. }
  359. case *parser.BinaryExpr:
  360. left, err := evalExprSimple(e.Left)
  361. if err != nil {
  362. return nil, err
  363. }
  364. right, err := evalExprSimple(e.Right)
  365. if err != nil {
  366. return nil, err
  367. }
  368. return evalBinarySimple(e.Op, left, right)
  369. case *parser.UnaryExpr:
  370. val, err := evalExprSimple(e.Operand)
  371. if err != nil {
  372. return nil, err
  373. }
  374. switch e.Op {
  375. case lexer.TokenMinus:
  376. return -toFloatSimple(val), nil
  377. case lexer.TokenNOT:
  378. return !toBoolSimple(val), nil
  379. }
  380. return val, nil
  381. case *parser.ParenExpr:
  382. return evalExprSimple(e.Expr)
  383. }
  384. return nil, fmt.Errorf("unsupported expression type: %T", expr)
  385. }
  386. func evalBinarySimple(op lexer.TokenType, left, right interface{}) (interface{}, error) {
  387. switch op {
  388. case lexer.TokenPlus:
  389. return toFloatSimple(left) + toFloatSimple(right), nil
  390. case lexer.TokenMinus:
  391. return toFloatSimple(left) - toFloatSimple(right), nil
  392. case lexer.TokenStar:
  393. return toFloatSimple(left) * toFloatSimple(right), nil
  394. case lexer.TokenSlash:
  395. r := toFloatSimple(right)
  396. if r == 0 {
  397. return nil, nil
  398. }
  399. return toFloatSimple(left) / r, nil
  400. case lexer.TokenEq:
  401. return compareSimple(left, right) == 0, nil
  402. case lexer.TokenNeq:
  403. return compareSimple(left, right) != 0, nil
  404. case lexer.TokenLt:
  405. return compareSimple(left, right) < 0, nil
  406. case lexer.TokenGt:
  407. return compareSimple(left, right) > 0, nil
  408. case lexer.TokenLte:
  409. return compareSimple(left, right) <= 0, nil
  410. case lexer.TokenGte:
  411. return compareSimple(left, right) >= 0, nil
  412. case lexer.TokenAND:
  413. return toBoolSimple(left) && toBoolSimple(right), nil
  414. case lexer.TokenOR:
  415. return toBoolSimple(left) || toBoolSimple(right), nil
  416. }
  417. return nil, fmt.Errorf("unsupported operator: %v", op)
  418. }
  419. func toFloatSimple(v interface{}) float64 {
  420. switch val := v.(type) {
  421. case int64:
  422. return float64(val)
  423. case float64:
  424. return val
  425. case bool:
  426. if val {
  427. return 1
  428. }
  429. return 0
  430. }
  431. return 0
  432. }
  433. func toBoolSimple(v interface{}) bool {
  434. switch val := v.(type) {
  435. case bool:
  436. return val
  437. case int64:
  438. return val != 0
  439. case float64:
  440. return val != 0
  441. }
  442. return false
  443. }
  444. func compareSimple(a, b interface{}) int {
  445. fa := toFloatSimple(a)
  446. fb := toFloatSimple(b)
  447. if fa < fb {
  448. return -1
  449. }
  450. if fa > fb {
  451. return 1
  452. }
  453. return 0
  454. }
  455. func printHelp() {
  456. fmt.Println("PizzaSQL Commands:")
  457. fmt.Println(" help, \\h Show this help")
  458. fmt.Println(" quit, \\q Exit the program")
  459. fmt.Println(" tables, \\dt List all tables")
  460. fmt.Println(" clear, \\c Clear the input buffer")
  461. fmt.Println()
  462. fmt.Println("SQL Statements (end with semicolon):")
  463. fmt.Println(" SELECT ... FROM ... WHERE ...")
  464. fmt.Println(" INSERT INTO table (cols) VALUES (...)")
  465. fmt.Println(" UPDATE table SET col = val WHERE ...")
  466. fmt.Println(" DELETE FROM table WHERE ...")
  467. fmt.Println(" CREATE TABLE table (col TYPE, ...)")
  468. fmt.Println(" DROP TABLE table")
  469. fmt.Println()
  470. fmt.Println("Expression Mode (SELECT without FROM):")
  471. fmt.Println(" SELECT 1 + 2 * 3;")
  472. fmt.Println(" SELECT UPPER('hello');")
  473. fmt.Println()
  474. fmt.Println("Export/Import:")
  475. fmt.Println(" pizzasql -db mydb -o backup.sql Export database to SQL file")
  476. fmt.Println(" pizzasql -db mydb -table users -o t.sql Export single table")
  477. fmt.Println(" pizzasql -db mydb -o backup.sql -drop Include DROP TABLE statements")
  478. fmt.Println(" pizzasql -db mydb -i backup.sql Import SQL file")
  479. fmt.Println(" pizzasql -db mydb -i backup.sql -ignore-errors Continue on errors")
  480. fmt.Println()
  481. fmt.Println("CSV Format:")
  482. fmt.Println(" pizzasql -db mydb -table users -o users.csv Export table to CSV")
  483. fmt.Println(" pizzasql -db mydb -table users -i users.csv Import CSV to table")
  484. fmt.Println(" pizzasql -db mydb -table new -i data.csv -create-table Create table from CSV")
  485. }
  486. func listTables(schema *storage.SchemaManager) {
  487. tables, err := schema.ListTables()
  488. if err != nil {
  489. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  490. return
  491. }
  492. if len(tables) == 0 {
  493. fmt.Println("No tables found")
  494. return
  495. }
  496. fmt.Println("Tables:")
  497. for _, t := range tables {
  498. fmt.Printf(" %s\n", t)
  499. }
  500. }
  501. func runExport() {
  502. // Connect to PizzaKV
  503. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  504. if err != nil {
  505. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  506. os.Exit(1)
  507. }
  508. defer pool.Close()
  509. schema := storage.NewSchemaManager(pool, *database)
  510. table := storage.NewTableManager(pool, schema, *database)
  511. // Determine format from flag or file extension
  512. format := strings.ToLower(*exportFormat)
  513. if format == "" {
  514. format = detectFileFormat(*exportFile)
  515. }
  516. switch format {
  517. case "csv":
  518. // CSV export requires a table name
  519. if *exportTable == "" {
  520. fmt.Fprintf(os.Stderr, "CSV export requires -table flag\n")
  521. os.Exit(1)
  522. }
  523. csvOpts := csvexport.DefaultExportOptions()
  524. csvOpts.Table = *exportTable
  525. data, err := csvexport.ExportTableToBytes(schema, table, csvOpts)
  526. if err != nil {
  527. fmt.Fprintf(os.Stderr, "Export failed: %v\n", err)
  528. os.Exit(1)
  529. }
  530. err = os.WriteFile(*exportFile, data, 0644)
  531. if err != nil {
  532. fmt.Fprintf(os.Stderr, "Failed to write file: %v\n", err)
  533. os.Exit(1)
  534. }
  535. fmt.Printf("Exported table '%s' to %s (CSV)\n", *exportTable, *exportFile)
  536. default: // sql, sqlite
  537. // Configure export options
  538. opts := sqlexport.ExportOptions{
  539. IncludeData: true,
  540. DropTables: *exportDrop,
  541. }
  542. if *exportTable != "" {
  543. opts.Tables = []string{*exportTable}
  544. }
  545. // Export database
  546. sql, err := sqlexport.ExportDatabase(schema, table, opts)
  547. if err != nil {
  548. fmt.Fprintf(os.Stderr, "Export failed: %v\n", err)
  549. os.Exit(1)
  550. }
  551. // Write to file
  552. err = os.WriteFile(*exportFile, []byte(sql), 0644)
  553. if err != nil {
  554. fmt.Fprintf(os.Stderr, "Failed to write file: %v\n", err)
  555. os.Exit(1)
  556. }
  557. fmt.Printf("Exported database '%s' to %s\n", *database, *exportFile)
  558. }
  559. }
  560. func runImport() {
  561. // Connect to PizzaKV
  562. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  563. if err != nil {
  564. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  565. os.Exit(1)
  566. }
  567. defer pool.Close()
  568. schema := storage.NewSchemaManager(pool, *database)
  569. table := storage.NewTableManager(pool, schema, *database)
  570. exec := executor.New(schema, table)
  571. exec.SyncCatalog()
  572. // Read file
  573. data, err := os.ReadFile(*importFile)
  574. if err != nil {
  575. fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err)
  576. os.Exit(1)
  577. }
  578. // Determine format from flag or file extension
  579. format := strings.ToLower(*exportFormat)
  580. if format == "" {
  581. format = detectFileFormat(*importFile)
  582. }
  583. switch format {
  584. case "csv":
  585. // CSV import requires a table name
  586. if *exportTable == "" {
  587. fmt.Fprintf(os.Stderr, "CSV import requires -table flag\n")
  588. os.Exit(1)
  589. }
  590. csvOpts := csvimport.DefaultImportOptions()
  591. csvOpts.TableName = *exportTable
  592. csvOpts.IgnoreErrors = *ignoreErrors
  593. csvOpts.CreateTable = *createTable
  594. result, err := csvimport.ImportCSV(strings.NewReader(string(data)), schema, table, csvOpts)
  595. if err != nil {
  596. fmt.Fprintf(os.Stderr, "Import failed: %v\n", err)
  597. if len(result.Errors) > 0 {
  598. fmt.Fprintf(os.Stderr, "Errors:\n")
  599. for _, e := range result.Errors {
  600. fmt.Fprintf(os.Stderr, " - %s\n", e)
  601. }
  602. }
  603. os.Exit(1)
  604. }
  605. fmt.Printf("CSV import completed successfully\n")
  606. fmt.Printf(" Rows imported: %d\n", result.RowsImported)
  607. if result.RowsSkipped > 0 {
  608. fmt.Printf(" Rows skipped: %d\n", result.RowsSkipped)
  609. }
  610. if result.TableCreated {
  611. fmt.Printf(" Table created: %s\n", *exportTable)
  612. }
  613. if len(result.Errors) > 0 {
  614. fmt.Printf(" Warnings/Errors: %d\n", len(result.Errors))
  615. for _, e := range result.Errors {
  616. fmt.Printf(" - %s\n", e)
  617. }
  618. }
  619. default: // sql, sqlite
  620. // Configure import options
  621. opts := sqlimport.ImportOptions{
  622. IgnoreErrors: *ignoreErrors,
  623. }
  624. // Import SQL
  625. result, err := sqlimport.ImportSQL(exec, string(data), opts)
  626. if err != nil {
  627. fmt.Fprintf(os.Stderr, "Import failed: %v\n", err)
  628. if len(result.Errors) > 0 {
  629. fmt.Fprintf(os.Stderr, "Errors:\n")
  630. for _, e := range result.Errors {
  631. fmt.Fprintf(os.Stderr, " - %s\n", e)
  632. }
  633. }
  634. os.Exit(1)
  635. }
  636. fmt.Printf("Import completed successfully\n")
  637. fmt.Printf(" Statements executed: %d\n", result.StatementsExecuted)
  638. if len(result.TablesCreated) > 0 {
  639. fmt.Printf(" Tables created: %s\n", strings.Join(result.TablesCreated, ", "))
  640. }
  641. if len(result.TablesDropped) > 0 {
  642. fmt.Printf(" Tables dropped: %s\n", strings.Join(result.TablesDropped, ", "))
  643. }
  644. fmt.Printf(" Rows inserted: %d\n", result.RowsInserted)
  645. if len(result.Errors) > 0 {
  646. fmt.Printf(" Warnings/Errors: %d\n", len(result.Errors))
  647. for _, e := range result.Errors {
  648. fmt.Printf(" - %s\n", e)
  649. }
  650. }
  651. }
  652. }
  653. func detectFileFormat(filename string) string {
  654. lower := strings.ToLower(filename)
  655. if strings.HasSuffix(lower, ".csv") {
  656. return "csv"
  657. }
  658. if strings.HasSuffix(lower, ".db") || strings.HasSuffix(lower, ".sqlite") || strings.HasSuffix(lower, ".sqlite3") {
  659. return "sqlite"
  660. }
  661. return "sql"
  662. }
  663. func runHTTPServer() {
  664. // Connect to PizzaKV
  665. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  666. if err != nil {
  667. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  668. fmt.Fprintf(os.Stderr, "Make sure PizzaKV is running: pizzakv\n")
  669. os.Exit(1)
  670. }
  671. defer pool.Close()
  672. // Create database manager for multi-database support
  673. dbManagerConfig := &storage.DatabaseManagerConfig{
  674. DefaultDatabase: *database,
  675. AutoCreate: true, // Auto-create databases on first access
  676. }
  677. dbManager := storage.NewDatabaseManager(pool, dbManagerConfig)
  678. // Configure HTTP server
  679. config := httpserver.DefaultConfig()
  680. config.Host = *httpHost
  681. config.Port = *httpPort
  682. config.EnableCORS = *httpCORS
  683. config.EnableAuth = *httpAuth
  684. config.EnableCompression = *httpCompression
  685. config.EnableLogging = !*quiet
  686. if *apiKeys != "" {
  687. config.APIKeys = strings.Split(*apiKeys, ",")
  688. }
  689. // Create and start server with multi-database support
  690. server := httpserver.NewWithDatabaseManager(config, dbManager)
  691. var pprofServer *http.Server
  692. if startPprofServerHook != nil {
  693. pprofServer = startPprofServerHook()
  694. }
  695. // Handle graceful shutdown
  696. stop := make(chan os.Signal, 1)
  697. signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
  698. // Start server in goroutine
  699. go func() {
  700. if err := server.Start(); err != nil && err != http.ErrServerClosed {
  701. fmt.Fprintf(os.Stderr, "HTTP server error: %v\n", err)
  702. os.Exit(1)
  703. }
  704. }()
  705. fmt.Printf("PizzaSQL HTTP server started on http://%s:%d\n", *httpHost, *httpPort)
  706. if pprofServer != nil {
  707. fmt.Printf("pprof debug server started on http://%s/debug/pprof/\n", pprofServer.Addr)
  708. }
  709. fmt.Printf("Default database: %s\n", *database)
  710. fmt.Printf("PizzaKV: %s\n", *kvAddr)
  711. fmt.Println()
  712. fmt.Println("Multi-database support enabled!")
  713. fmt.Println("Use the X-Database header to select a database per request.")
  714. fmt.Println()
  715. fmt.Println("Endpoints:")
  716. fmt.Println(" POST /query - Execute SQL query")
  717. fmt.Println(" POST /execute - Batch execution")
  718. fmt.Println(" GET /schema/tables - List tables")
  719. fmt.Println(" GET /schema/tables/{name} - Table schema")
  720. fmt.Println(" GET /health - Health check")
  721. fmt.Println(" GET /stats - Statistics")
  722. fmt.Println(" GET /metrics - Prometheus metrics")
  723. fmt.Println(" POST /transaction/begin - Begin transaction")
  724. fmt.Println(" POST /transaction/commit - Commit transaction")
  725. fmt.Println(" POST /transaction/rollback - Rollback transaction")
  726. fmt.Println()
  727. fmt.Println("Examples:")
  728. fmt.Printf(" # Query default database\n")
  729. fmt.Printf(" curl -X POST http://%s:%d/query -H 'Content-Type: application/json' -d '{\"sql\":\"SELECT 1+1\"}'\n", *httpHost, *httpPort)
  730. fmt.Println()
  731. fmt.Printf(" # Query specific database using X-Database header\n")
  732. fmt.Printf(" curl -X POST http://%s:%d/query -H 'Content-Type: application/json' -H 'X-Database: tenant_db' -d '{\"sql\":\"SELECT * FROM users\"}'\n", *httpHost, *httpPort)
  733. fmt.Println()
  734. fmt.Println("Press Ctrl+C to stop")
  735. <-stop
  736. fmt.Println("\nShutting down server...")
  737. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  738. defer cancel()
  739. if err := server.Shutdown(ctx); err != nil {
  740. fmt.Fprintf(os.Stderr, "Error during shutdown: %v\n", err)
  741. }
  742. if pprofServer != nil {
  743. if err := pprofServer.Shutdown(ctx); err != nil {
  744. fmt.Fprintf(os.Stderr, "Error during pprof shutdown: %v\n", err)
  745. }
  746. }
  747. fmt.Println("Server stopped")
  748. }
  749. func runPGServer() {
  750. // Connect to PizzaKV
  751. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  752. if err != nil {
  753. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  754. fmt.Fprintf(os.Stderr, "Make sure PizzaKV is running: pizzakv\n")
  755. os.Exit(1)
  756. }
  757. defer pool.Close()
  758. // Create database manager for multi-database support
  759. dbManagerConfig := &storage.DatabaseManagerConfig{
  760. DefaultDatabase: *database,
  761. AutoCreate: true,
  762. }
  763. dbManager := storage.NewDatabaseManager(pool, dbManagerConfig)
  764. // Configure PostgreSQL server
  765. config := pgserver.DefaultConfig()
  766. config.Host = *pgHost
  767. config.Port = *pgPort
  768. config.DefaultDatabase = *database
  769. config.Quiet = *quiet
  770. // Create and start server
  771. server := pgserver.New(config, dbManager)
  772. // Handle graceful shutdown
  773. stop := make(chan os.Signal, 1)
  774. signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
  775. // Start server in goroutine
  776. go func() {
  777. if err := server.Start(); err != nil {
  778. fmt.Fprintf(os.Stderr, "PostgreSQL server error: %v\n", err)
  779. os.Exit(1)
  780. }
  781. }()
  782. fmt.Printf("PizzaSQL PostgreSQL Server started on %s:%d\n", *pgHost, *pgPort)
  783. fmt.Printf("Default database: %s\n", *database)
  784. fmt.Printf("PizzaKV: %s\n", *kvAddr)
  785. fmt.Println()
  786. fmt.Println("Connect using psql:")
  787. fmt.Printf(" psql -h %s -p %d -d %s\n", *pgHost, *pgPort, *database)
  788. fmt.Println()
  789. fmt.Println("Or any PostgreSQL client library:")
  790. fmt.Printf(" postgresql://%s:%d/%s\n", *pgHost, *pgPort, *database)
  791. fmt.Println()
  792. fmt.Println("Press Ctrl+C to stop")
  793. <-stop
  794. fmt.Println("\nShutting down server...")
  795. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  796. defer cancel()
  797. if err := server.Shutdown(ctx); err != nil {
  798. fmt.Fprintf(os.Stderr, "Error during shutdown: %v\n", err)
  799. }
  800. fmt.Println("Server stopped")
  801. }
  802. // launchPizzaKV starts a PizzaKV instance and updates kvAddr
  803. func launchPizzaKV() error {
  804. kvManager = kvmanager.NewManager()
  805. kvManager.SetInfoFile(*kvInfoFile)
  806. // Clean up any stale process info
  807. if err := kvmanager.CleanupStaleProcess(*kvInfoFile); err != nil {
  808. // Check if it's an "already running" error
  809. if strings.Contains(err.Error(), "already running") {
  810. fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
  811. fmt.Fprintf(os.Stderr, "\nOptions:\n")
  812. fmt.Fprintf(os.Stderr, " 1. Use the existing instance: remove -kv flag and use -kvaddr=localhost:<port>\n")
  813. fmt.Fprintf(os.Stderr, " 2. Stop it: kill %d\n", getPIDFromFile(*kvInfoFile))
  814. fmt.Fprintf(os.Stderr, " 3. Delete the info file: rm %s\n\n", *kvInfoFile)
  815. return err
  816. }
  817. return fmt.Errorf("failed to cleanup stale process: %w", err)
  818. }
  819. fmt.Println("Starting PizzaKV...")
  820. info, err := kvManager.Start(*kvFlags)
  821. if err != nil {
  822. return err
  823. }
  824. fmt.Printf("\nPizzaKV started on %s (PID: %d)\n", info.Addr, info.PID)
  825. fmt.Printf("Info written to: %s\n", *kvInfoFile)
  826. fmt.Println("PizzaKV is ready!")
  827. // Update kvAddr to use the launched instance
  828. *kvAddr = info.Addr
  829. return nil
  830. }
  831. // stopPizzaKV stops the managed PizzaKV instance
  832. func stopPizzaKV() {
  833. if kvManager != nil {
  834. fmt.Println("Stopping PizzaKV...")
  835. if err := kvManager.Stop(); err != nil {
  836. fmt.Fprintf(os.Stderr, "Error stopping PizzaKV: %v\n", err)
  837. } else {
  838. fmt.Println("PizzaKV stopped")
  839. }
  840. }
  841. }
  842. // getPIDFromFile reads the PID from the info file
  843. func getPIDFromFile(path string) int {
  844. mgr := kvmanager.NewManager()
  845. mgr.SetInfoFile(path)
  846. info, err := mgr.LoadInfo()
  847. if err != nil {
  848. return 0
  849. }
  850. return info.PID
  851. }