demo.vala 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using Invercargill;
  2. using Invercargill.DataStructures;
  3. using Invercargill.Expressions;
  4. using InvercargillSql;
  5. using InvercargillSql.Orm;
  6. using InvercargillSql.Orm.Projections;
  7. using InvercargillSql.Migrations;
  8. using InvercargillSql.Dialects;
  9. /**
  10. * Invercargill-Sql ORM Demo Application
  11. *
  12. * This demo showcases the main features of the Invercargill-Sql ORM:
  13. * - Database migrations
  14. * - Entity mapping and CRUD operations
  15. * - Projection queries (simple, join, aggregate)
  16. */
  17. public int main(string[] args) {
  18. print("=== Invercargill-Sql ORM Demo ===\n\n");
  19. // Allow optional connection string argument
  20. string connection_string = args.length > 1
  21. ? args[1]
  22. : "sqlite::memory:";
  23. try {
  24. // Create and open database connection
  25. var conn = ConnectionFactory.create_and_open(connection_string);
  26. // Pick the dialect from the connection string scheme so the same
  27. // demo runs unchanged against SQLite or PostgreSQL.
  28. bool is_pg = connection_string.has_prefix("postgres://")
  29. || connection_string.has_prefix("postgresql://");
  30. var dialect = is_pg
  31. ? DialectFactory.create("postgresql")
  32. : DialectFactory.create("sqlite");
  33. if (dialect == null) {
  34. dialect = new SqliteDialect();
  35. }
  36. var registry = new TypeRegistry();
  37. // --- Running Migrations ---
  38. print("--- Running Migrations ---\n");
  39. run_migrations(conn, dialect);
  40. // --- Registering Entity Mappers ---
  41. print("\n--- Registering Entity Mappers ---\n");
  42. register_entities(registry, conn, dialect);
  43. // --- Registering Projection Definitions ---
  44. print("Registering projection definitions...\n");
  45. register_projections(registry);
  46. // Create session with registry
  47. var session = new OrmSession(conn, registry, dialect);
  48. // --- Inserting Data ---
  49. print("\n--- Inserting Data ---\n");
  50. insert_sample_data(session);
  51. // --- Querying Entities ---
  52. print("\n--- Querying Entities ---\n");
  53. demonstrate_entity_queries(session);
  54. // --- Updating Data ---
  55. print("\n--- Updating Data ---\n");
  56. demonstrate_update(session);
  57. // --- Deleting Data ---
  58. print("\n--- Deleting Data ---\n");
  59. demonstrate_delete(session);
  60. // --- Querying Projections ---
  61. print("\n--- Querying Projections ---\n");
  62. demonstrate_projection_queries(session);
  63. print("\n=== Demo Complete ===\n");
  64. conn.close();
  65. return 0;
  66. } catch (Error e) {
  67. printerr("\n=== Demo failed: %s ===\n", e.message);
  68. return 1;
  69. }
  70. }
  71. void run_migrations(Connection conn, SqlDialect dialect) throws SqlError {
  72. var runner = new MigrationRunner(conn, dialect);
  73. // Register migrations
  74. runner.register_migration(new V001_CreateUsers());
  75. runner.register_migration(new V002_CreateProducts());
  76. runner.register_migration(new V003_CreateOrders());
  77. // Run all pending migrations
  78. runner.migrate_to_latest();
  79. // Get applied migrations count
  80. var applied = runner.get_applied_migrations();
  81. print("Applied %d migrations\n", (int)applied.length);
  82. }
  83. void register_entities(TypeRegistry registry, Connection conn, SqlDialect dialect) throws SqlError {
  84. // Register User entity with schema introspection
  85. registry.register_with_schema<User>("users", conn, dialect, User.configure_mapper);
  86. print("Registered User entity\n");
  87. // Register Product entity with schema introspection
  88. registry.register_with_schema<Product>("products", conn, dialect, Product.configure_mapper);
  89. print("Registered Product entity\n");
  90. // Register Order entity with schema introspection
  91. registry.register_with_schema<Order>("orders", conn, dialect, Order.configure_mapper);
  92. print("Registered Order entity\n");
  93. }
  94. void register_projections(TypeRegistry registry) throws ProjectionError {
  95. // Register projections using static configure_projection methods
  96. var user_summary_builder = new ProjectionBuilder<UserSummary>(registry);
  97. UserSummary.configure_projection(user_summary_builder);
  98. registry.register_projection<UserSummary>(user_summary_builder.build());
  99. var order_detail_builder = new ProjectionBuilder<OrderDetail>(registry);
  100. OrderDetail.configure_projection(order_detail_builder);
  101. registry.register_projection<OrderDetail>(order_detail_builder.build());
  102. var sales_report_builder = new ProjectionBuilder<SalesReport>(registry);
  103. SalesReport.configure_projection(sales_report_builder);
  104. registry.register_projection<SalesReport>(sales_report_builder.build());
  105. }
  106. void insert_sample_data(OrmSession session) throws SqlError {
  107. // Insert users
  108. var alice = new User();
  109. alice.name = "Alice";
  110. alice.email = "alice@example.com";
  111. alice.age = 30;
  112. alice.is_active = true;
  113. session.insert(alice);
  114. print("Inserted user: %s (ID: %lld)\n", alice.name, alice.id);
  115. var bob = new User();
  116. bob.name = "Bob";
  117. bob.email = "bob@example.com";
  118. bob.age = 22;
  119. bob.is_active = true;
  120. session.insert(bob);
  121. print("Inserted user: %s (ID: %lld)\n", bob.name, bob.id);
  122. var charlie = new User();
  123. charlie.name = "Charlie";
  124. charlie.email = "charlie@example.com";
  125. charlie.age = 35;
  126. charlie.is_active = false;
  127. session.insert(charlie);
  128. print("Inserted user: %s (ID: %lld)\n", charlie.name, charlie.id);
  129. // Insert products
  130. var widget = new Product();
  131. widget.name = "Widget";
  132. widget.category = "Electronics";
  133. widget.price = 29.99;
  134. widget.stock = 100;
  135. session.insert(widget);
  136. print("Inserted product: %s (ID: %lld)\n", widget.name, widget.id);
  137. var gadget = new Product();
  138. gadget.name = "Gadget";
  139. gadget.category = "Electronics";
  140. gadget.price = 49.99;
  141. gadget.stock = 50;
  142. session.insert(gadget);
  143. print("Inserted product: %s (ID: %lld)\n", gadget.name, gadget.id);
  144. var doohickey = new Product();
  145. doohickey.name = "Doohickey";
  146. doohickey.category = "Accessories";
  147. doohickey.price = 9.99;
  148. doohickey.stock = 200;
  149. session.insert(doohickey);
  150. print("Inserted product: %s (ID: %lld)\n", doohickey.name, doohickey.id);
  151. // Insert orders
  152. int order_count = 0;
  153. // Alice orders 2 Widgets
  154. var order1 = create_order(alice.id, widget.id, 2, 59.98, "completed");
  155. session.insert(order1);
  156. order_count++;
  157. // Alice orders 1 Gadget
  158. var order2 = create_order(alice.id, gadget.id, 1, 49.99, "completed");
  159. session.insert(order2);
  160. order_count++;
  161. // Bob orders 3 Doohickeys
  162. var order3 = create_order(bob.id, doohickey.id, 3, 29.97, "pending");
  163. session.insert(order3);
  164. order_count++;
  165. // Charlie orders 1 Widget and 2 Gadgets
  166. var order4 = create_order(charlie.id, widget.id, 1, 29.99, "completed");
  167. session.insert(order4);
  168. order_count++;
  169. var order5 = create_order(charlie.id, gadget.id, 2, 99.98, "completed");
  170. session.insert(order5);
  171. order_count++;
  172. print("Inserted %d orders\n", order_count);
  173. }
  174. Order create_order(int64 user_id, int64 product_id, int64 quantity, double total, string status) {
  175. var order = new Order();
  176. order.user_id = user_id;
  177. order.product_id = product_id;
  178. order.quantity = quantity;
  179. order.total = total;
  180. order.status = status;
  181. order.created_at = new DateTime.now_utc();
  182. return order;
  183. }
  184. void demonstrate_entity_queries(OrmSession session) throws SqlError {
  185. // Query all users
  186. var all_users = session.query<User>().materialise();
  187. print("All users: %d\n", (int)all_users.length);
  188. // Query users over 25
  189. var users_over_25 = session.query<User>()
  190. .where(expr("age > $0", new NativeElement<int?>(25)))
  191. .materialise();
  192. print("Users over 25: %d\n", (int)users_over_25.length);
  193. // Query active users ordered by age
  194. var active_users = session.query<User>()
  195. .where(expr("is_active == $0", new NativeElement<bool?>(true)))
  196. .order_by(expr("age"))
  197. .materialise();
  198. print("Active users ordered by age: ");
  199. var first = true;
  200. foreach (var user in active_users) {
  201. if (!first) print(", ");
  202. print("%s(%lld)", user.name, user.age);
  203. first = false;
  204. }
  205. print("\n");
  206. // Query with pagination
  207. var paged_users = session.query<User>()
  208. .order_by(expr("id"))
  209. .limit(2)
  210. .offset(1)
  211. .materialise();
  212. print("Users (page 2, size 2): %d\n", (int)paged_users.length);
  213. }
  214. void demonstrate_update(OrmSession session) throws SqlError {
  215. // Find Alice by ID
  216. var alice = session.query<User>()
  217. .where(expr("id == $0", new NativeElement<int64?>(1)))
  218. .first();
  219. if (alice != null) {
  220. // Update Alice's age
  221. alice.age = 31;
  222. session.update(alice);
  223. print("Updated %s's age to %lld\n", alice.name, alice.age);
  224. // Verify the update
  225. var verified = session.query<User>()
  226. .where(expr("id == $0", new NativeElement<int64?>(1)))
  227. .first();
  228. print("Verified update: %s is now %lld\n", verified.name, verified.age);
  229. }
  230. }
  231. void demonstrate_delete(OrmSession session) throws SqlError {
  232. // Find Bob by name (using age as a proxy since string comparison in expressions may not work)
  233. var bob = session.query<User>()
  234. .where(expr("age == $0", new NativeElement<int64?>(22)))
  235. .first();
  236. if (bob != null) {
  237. print("Deleted user: %s\n", bob.name);
  238. session.delete(bob);
  239. // Verify deletion
  240. var remaining = session.query<User>().materialise();
  241. print("Remaining users: %d\n", (int)remaining.length);
  242. }
  243. }
  244. void demonstrate_projection_queries(OrmSession session) throws SqlError, ProjectionError {
  245. // Simple projection - User summaries
  246. print("\n--- User Summaries ---\n");
  247. var user_summaries = session.query<UserSummary>()
  248. .order_by(expr("user_name"))
  249. .materialise();
  250. foreach (var summary in user_summaries) {
  251. print(" [%lld] %s <%s>\n", summary.user_id, summary.user_name, summary.email);
  252. }
  253. print("Total user summaries: %d\n", (int)user_summaries.length);
  254. // Join projection - Order details
  255. print("\n--- Order Details ---\n");
  256. var order_details = session.query<OrderDetail>()
  257. .order_by(expr("order_id"))
  258. .materialise();
  259. foreach (var detail in order_details) {
  260. print(" [%lld] %s ordered %dx %s ($%.2f) - %s\n",
  261. detail.order_id, detail.user_name, detail.quantity,
  262. detail.product_name, detail.total, detail.status);
  263. }
  264. print("Total order details: %d\n", (int)order_details.length);
  265. // Aggregate projection - Sales by category
  266. print("\n--- Sales by Category ---\n");
  267. var sales_reports = session.query<SalesReport>()
  268. .order_by_desc(expr("total_revenue"))
  269. .materialise();
  270. foreach (var report in sales_reports) {
  271. print(" %s: %lld orders, $%.2f revenue, $%.2f avg\n",
  272. report.category, report.total_orders,
  273. report.total_revenue, report.avg_order_value);
  274. }
  275. print("Total categories: %d\n", (int)sales_reports.length);
  276. // Projection with where clause
  277. print("\n--- Completed Orders ---\n");
  278. var completed_orders = session.query<OrderDetail>()
  279. .where(expr("status == $0", new NativeElement<string>("completed")))
  280. .materialise();
  281. print("Completed orders: %d\n", (int)completed_orders.length);
  282. }