module: sql-odbc-implementation Author: eec Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // $HopeName: !binding.dylan(D-kan.6) $ define sideways method default-conversion(value :: ) => (converted-value :: ) make(, year: value.year-value, month: value.month-value, day: value.day-value, hours: value.hour-value, minutes: value.minute-value, seconds: value.second-value, microseconds: 0) end method; define open generic sql-binding-info(object :: ) // should be sealed but due to a bug in the compiler... => (sql-data-type :: , precision :: , scale :: ); define method sql-binding-info(object == $null-value) => (sql-data-type :: , precision :: , scale :: ) sql-binding-info(0) // Since the value is null, we don't care // what we are binding to. end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-integer, 0, 0) end method; define method sql-binding-info(object :: big/) => (sql-data-type :: , precision :: , scale :: ) values($sql-integer, 0, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-char, 1, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-double, 5, 5); //+++ Correct precision and scale should be used! end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) //+ The let statement is needed to squelch a bogus type inference warning let string-size :: = object.size; values($sql-varchar, string-size, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-type-timestamp, 19, 0) end method; define generic create-storage(sql-data-type :: , precision :: , scale :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); define method create-storage(sql-data-type :: , precision :: , scale :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); signal(make(, format-string: "Binding to column whose datatype (%=) is not supported\n" "Using instance of instead.\n", format-arguments: sql-data-type)); values($sql-unsupported-type, null-pointer(), 0, 0); end method; define method create-storage(sql-data-type == $sql-unknown-type, precision :: , scale :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); signal(make(, format-string: "Attempting to bind to a column with a datatype that " "ODBC does not recognize.\n" "Using instance of instead.\n")); values($sql-unknown-type, null-pointer(), 0, 0); end method; define method create-storage(sql-data-type == $sql-char, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) // storage includes space for the null-termination byte let storage-size = precision + 1; let storage = make(, size: storage-size); if (initial-value ~= #f) pointer-value(storage, index: 0) := initial-value; end if; values($sql-c-char, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-type-date, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); let storage-size = size-of(); if (initial-value ~= #f) storage.year-value := initial-value.date-year; storage.month-value := initial-value.date-month; storage.day-value := initial-value.date-day; end if; values($sql-c-type-date, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-type-time, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); let storage-size = size-of(); if (initial-value ~= #f) storage.hour-value := initial-value.date-hours; storage.minute-value := initial-value.date-minutes; storage.second-value := initial-value.date-seconds; storage.fraction-value := 0; end if; values($sql-c-type-time, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-type-timestamp, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); let storage-size = size-of(); if (initial-value ~= #f) storage.year-value := initial-value.date-year; storage.month-value := initial-value.date-month; storage.day-value := initial-value.date-day; storage.hour-value := initial-value.date-hours; storage.minute-value := initial-value.date-minutes; storage.second-value := initial-value.date-seconds; storage.fraction-value := 0; end if; values($sql-c-type-timestamp, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-datetime, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); let storage-size = size-of(); if (initial-value ~= #f) storage.year-value := initial-value.date-year; storage.month-value := initial-value.date-month; storage.day-value := initial-value.date-day; storage.hour-value := initial-value.date-hours; storage.minute-value := initial-value.date-minutes; storage.second-value := initial-value.date-seconds; storage.fraction-value := 0; end if; values($sql-c-type-timestamp, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-numeric, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage-size = precision + 1; let storage = make(, size: storage-size); values($sql-c-char, storage, storage-size, precision); end method; define method create-storage(sql-data-type == $sql-decimal, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage-size = precision + 1; let storage = make(, size: storage-size); values($sql-c-char, storage, storage-size, precision); end method; /* Because you can't specialize a method on a keyword argument, we have three helper methods to actually create the appropriate storage. Big-integers need the c-raw versions of the storage in order to stash a machine-word into them; the non-raw (cooked?) versions of c storage automatically convert integers, and can't handle big-integers. */ define method create-storage-helper(value :: ) => result :: ; let storage = make(); storage; end; define method create-storage-helper(value :: ) => result :: ; let storage = make(); pointer-value(storage) := value; storage; end; define method create-storage-helper(value :: big/) => result :: ; let storage = make(); pointer-value(storage) := as(, value); storage; end; define method create-storage(sql-data-type == $sql-integer, precision :: , scale :: , #key initial-value :: false-or(big/)) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = create-storage-helper(initial-value); values($sql-c-long, storage, 4, 4); end method; define method create-storage(sql-data-type == $sql-smallint, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); if (initial-value ~= #f) pointer-value(storage) := initial-value; end if; values($sql-c-short, storage, 2, 2); end method; define method create-storage(sql-data-type == $sql-tinyint, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); if (initial-value ~= #f) pointer-value(storage) := initial-value; end if; values($sql-c-short, storage, 2, 2); end method; define method create-storage(sql-data-type == $sql-float, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); if (initial-value ~= #f) pointer-value(storage) := initial-value; end if; values($sql-c-double, storage, 4, 4); end method; define method create-storage(sql-data-type == $sql-real, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) create-storage($sql-double, precision, scale, initial-value: initial-value); end method; define method create-storage(sql-data-type == $sql-double, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let storage = make(); if (initial-value ~= #f) pointer-value(storage) := as(, initial-value); end if; values($sql-c-double, storage, 8, 8); end method; define method create-storage(sql-data-type == $sql-varchar, precision :: , scale :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) // string size includes the null termination byte which ODBC appends let storage-size = precision + 1; let storage = make(, size: storage-size); if (initial-value ~= #f) for (ndx from 0 to min(initial-value.size, storage-size) - 1) pointer-value(storage, index: ndx) := initial-value[ndx]; end for; end if; values($sql-c-char, storage, storage-size, precision); end method; define sealed concrete class () constant slot binding-name :: = "", init-keyword: name:; constant slot sql-data-type :: , required-init-keyword: sql-data-type:; slot c-data-type :: = $sql-c-char; constant slot precision :: , required-init-keyword: precision:; constant slot scale :: , required-init-keyword: scale:; // This slot isn't used anywhere. Should it be? //constant slot nullable :: , // init-keyword: nullable:, // init-value: #f; slot storage :: ; slot storage-size :: ; slot data-length :: = make(); end class; define method initialize (binding :: , #key initial-value) next-method(); let (the-data-type, a-storage, a-storage-size, a-data-size) = create-storage(binding.sql-data-type, binding.precision, binding.scale, initial-value: if (initial-value == $null-value) #f else initial-value end if); binding.c-data-type := the-data-type; binding.storage := a-storage; binding.storage-size := a-storage-size; if (initial-value == $null-value) pointer-value(binding.data-length) := $sql-null-data; else pointer-value(binding.data-length) := a-data-size; end if; finalize-when-unreachable(binding); end method; define method finalize(binding :: ) => () if (null-pointer?(binding.storage)) destroy(binding.storage); binding.storage := null-pointer(); end if; if (null-pointer?(binding.data-length)) destroy(binding.data-length); binding.data-length := null-pointer(); end if; notify-of-finalization(binding); next-method(); end method;